@git-stats-components/vue 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -0
- package/dist/style.css +1 -0
- package/dist/vue/src/components/ContributionGraph.vue.d.ts +67 -0
- package/dist/vue/src/components/StatsBreakdown.vue.d.ts +63 -0
- package/dist/vue/src/composables/useGitStats.d.ts +74 -0
- package/dist/vue/src/index.d.ts +12 -0
- package/dist/vue/src/types.d.ts +14 -0
- package/dist/vue-git-stats.es.js +636 -0
- package/dist/vue-git-stats.es.js.map +1 -0
- package/dist/vue-git-stats.umd.js +2 -0
- package/dist/vue-git-stats.umd.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.graph-content-wrapper[data-v-f1ff100d]{justify-items:anchor-center}.git-contribution-graph[data-v-f1ff100d]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif;font-size:12px;background:transparent;color:#e6edf3;padding:16px;max-width:1200px;margin:0 auto;width:100%}.graph-header[data-v-f1ff100d]{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.contribution-count[data-v-f1ff100d]{margin:0 0 4px;font-size:16px;font-weight:600}.data-source-text[data-v-f1ff100d]{color:#7d8590}.data-source-text.is-dummy[data-v-f1ff100d]{color:#f85149;font-weight:600;background:#f851491a;padding:2px 8px;border-radius:4px}.settings-btn[data-v-f1ff100d]{background:transparent;border:1px solid #30363d;color:#7d8590;padding:6px 12px;border-radius:6px;cursor:pointer;font-size:12px}.settings-btn[data-v-f1ff100d]:hover{background:#21262d;color:#e6edf3}.header-actions[data-v-f1ff100d]{position:relative}.settings-dropdown[data-v-f1ff100d]{position:absolute;right:0;top:100%;margin-top:4px;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:4px;z-index:10;min-width:150px}.settings-item[data-v-f1ff100d]{display:block;width:100%;background:transparent;border:none;color:#e6edf3;padding:8px 12px;text-align:left;cursor:pointer;border-radius:4px}.settings-item[data-v-f1ff100d]:hover{background:#30363d}.loading-state[data-v-f1ff100d]{display:flex;align-items:center;justify-content:center;gap:12px;padding:40px;color:#7d8590}.spinner[data-v-f1ff100d]{width:20px;height:20px;border:2px solid #30363d;border-top-color:#58a6ff;border-radius:50%;animation:spin-f1ff100d 1s linear infinite}@keyframes spin-f1ff100d{to{transform:rotate(360deg)}}.months-row[data-v-f1ff100d]{display:flex;margin-bottom:4px}.month-spacer[data-v-f1ff100d]{width:27px;flex-shrink:0}.months-container[data-v-f1ff100d]{display:grid;grid-template-columns:repeat(53,11px);gap:2px;flex:1;margin-left:3px;min-width:0}.month-label[data-v-f1ff100d]{font-size:11px;color:#7d8590;text-align:left}.grid-container[data-v-f1ff100d]{display:flex;gap:3px;min-width:fit-content}.day-labels[data-v-f1ff100d]{display:flex;flex-direction:column;width:24px;gap:2px;flex-shrink:0}.day-label[data-v-f1ff100d]{height:11px;font-size:9px;color:#7d8590;display:flex;align-items:center}.contribution-grid[data-v-f1ff100d]{display:flex;gap:2px;flex:1;min-width:0}.contribution-week[data-v-f1ff100d]{display:flex;flex-direction:column;gap:2px;flex-shrink:0}.contribution-day[data-v-f1ff100d]{width:11px;height:11px;border-radius:2px;cursor:pointer;outline:1px solid rgba(27,31,36,.06);outline-offset:-1px;flex-shrink:0}.contribution-day.level-0.green[data-v-f1ff100d]{background-color:#161b22}.contribution-day.level-1.green[data-v-f1ff100d]{background-color:#0e4429}.contribution-day.level-2.green[data-v-f1ff100d]{background-color:#006d32}.contribution-day.level-3.green[data-v-f1ff100d]{background-color:#26a641}.contribution-day.level-4.green[data-v-f1ff100d]{background-color:#39d353}.contribution-day.level-0.blue[data-v-f1ff100d]{background-color:#161b22}.contribution-day.level-1.blue[data-v-f1ff100d]{background-color:#0a3069}.contribution-day.level-2.blue[data-v-f1ff100d]{background-color:#1f6feb}.contribution-day.level-3.blue[data-v-f1ff100d]{background-color:#58a6ff}.contribution-day.level-4.blue[data-v-f1ff100d]{background-color:#79c0ff}.contribution-day.level-0.purple[data-v-f1ff100d]{background-color:#161b22}.contribution-day.level-1.purple[data-v-f1ff100d]{background-color:#3b1e6d}.contribution-day.level-2.purple[data-v-f1ff100d]{background-color:#8250df}.contribution-day.level-3.purple[data-v-f1ff100d]{background-color:#a475f9}.contribution-day.level-4.purple[data-v-f1ff100d]{background-color:#d2a8ff}.contribution-day.level-0.orange[data-v-f1ff100d]{background-color:#161b22}.contribution-day.level-1.orange[data-v-f1ff100d]{background-color:#7d2d00}.contribution-day.level-2.orange[data-v-f1ff100d]{background-color:#da7b00}.contribution-day.level-3.orange[data-v-f1ff100d]{background-color:#ffa348}.contribution-day.level-4.orange[data-v-f1ff100d]{background-color:#ffb366}.contribution-day[data-v-f1ff100d]:hover{outline:1px solid #c9d1d9;outline-offset:-1px}.graph-footer[data-v-f1ff100d]{display:flex;justify-content:space-between;align-items:center;margin-top:8px}.last-updated[data-v-f1ff100d]{color:#7d8590}.legend[data-v-f1ff100d]{display:flex;align-items:center;gap:4px}.legend-label[data-v-f1ff100d]{color:#7d8590}.legend-squares[data-v-f1ff100d]{display:flex;gap:2px}.legend-squares .contribution-day[data-v-f1ff100d]{cursor:default}.legend-squares .contribution-day[data-v-f1ff100d]:hover{outline:none}@media (max-width: 768px){.git-contribution-graph[data-v-f1ff100d]{padding:12px;font-size:11px;overflow-x:auto}.months-container[data-v-f1ff100d]{grid-template-columns:repeat(53,10px);gap:1px}.grid-container[data-v-f1ff100d]{gap:2px}.day-labels[data-v-f1ff100d]{width:20px}.day-label[data-v-f1ff100d]{height:10px;font-size:8px}.contribution-grid[data-v-f1ff100d],.contribution-week[data-v-f1ff100d]{gap:1px}.contribution-day[data-v-f1ff100d]{width:10px;height:10px}.settings-btn[data-v-f1ff100d]{font-size:10px;padding:4px 8px}.contribution-count[data-v-f1ff100d]{font-size:14px}}@media (max-width: 480px){.graph-header[data-v-f1ff100d]{flex-direction:column;align-items:flex-start;gap:8px}}.git-stats-breakdown[data-v-97689125]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif;padding:40px 20px;max-width:1200px;margin:0 auto}.stats-grid[data-v-97689125]{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:24px;margin-bottom:24px}.stat-card[data-v-97689125]{display:flex;align-items:center;gap:16px;padding:24px;background:#ffffff0d;border-radius:12px;border:1px solid rgba(255,255,255,.1);transition:all .3s ease}.stat-card[data-v-97689125]:hover{background:#ffffff14;border-color:#fff3;transform:translateY(-2px)}.stat-icon[data-v-97689125]{font-size:48px;line-height:1;opacity:.9;flex-shrink:0}.stat-content[data-v-97689125]{flex:1;min-width:0}.stat-value[data-v-97689125]{font-size:32px;font-weight:700;line-height:1.2;color:#e6edf3;margin-bottom:4px}.stat-label[data-v-97689125]{font-size:14px;color:#7d8590;text-transform:uppercase;letter-spacing:.5px}.stat-loading[data-v-97689125]{display:flex;align-items:center;justify-content:center;height:38px}.spinner[data-v-97689125]{width:24px;height:24px;border:3px solid rgba(255,255,255,.1);border-top-color:#58a6ff;border-radius:50%;animation:spin-97689125 1s linear infinite}@keyframes spin-97689125{to{transform:rotate(360deg)}}.stats-footer[data-v-97689125]{display:flex;flex-direction:column;align-items:center;gap:8px;padding-top:16px;border-top:1px solid rgba(255,255,255,.1)}.data-source[data-v-97689125]{font-size:12px;color:#7d8590;text-align:center}@media (max-width: 768px){.git-stats-breakdown[data-v-97689125]{padding:20px 12px}.stats-grid[data-v-97689125]{grid-template-columns:1fr;gap:16px}.stat-card[data-v-97689125]{padding:16px}.stat-icon[data-v-97689125]{font-size:36px}.stat-value[data-v-97689125]{font-size:24px}.stat-label[data-v-97689125]{font-size:12px}}@media (max-width: 480px){.stat-card[data-v-97689125]{flex-direction:column;text-align:center}.stat-content[data-v-97689125]{width:100%}}:root{--vgs-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;--vgs-border-radius: 6px;--vgs-spacing: 16px}.git-contribution-graph *,.git-stats-breakdown *{box-sizing:border-box}.vgs-loading{display:flex;align-items:center;justify-content:center;padding:40px}.vgs-error{color:#f85149;padding:16px;border:1px solid #f85149;border-radius:var(--vgs-border-radius);background:#f851491a}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ColorScheme } from '../../../core/src';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
dataUrl?: string;
|
|
5
|
+
profileIndex?: number;
|
|
6
|
+
colorScheme?: ColorScheme;
|
|
7
|
+
showSettings?: boolean;
|
|
8
|
+
cacheTTL?: number;
|
|
9
|
+
}
|
|
10
|
+
declare function __VLS_template(): {
|
|
11
|
+
"settings-icon"?(_: {}): any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_component: import('vue').DefineComponent<import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<Props>, {
|
|
14
|
+
dataUrl: string;
|
|
15
|
+
profileIndex: number;
|
|
16
|
+
colorScheme: string;
|
|
17
|
+
showSettings: boolean;
|
|
18
|
+
cacheTTL: number;
|
|
19
|
+
}>>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
20
|
+
"day-click": (data: {
|
|
21
|
+
date: string;
|
|
22
|
+
count: number;
|
|
23
|
+
}) => void;
|
|
24
|
+
"color-scheme-change": (scheme: ColorScheme) => void;
|
|
25
|
+
}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<Props>, {
|
|
26
|
+
dataUrl: string;
|
|
27
|
+
profileIndex: number;
|
|
28
|
+
colorScheme: string;
|
|
29
|
+
showSettings: boolean;
|
|
30
|
+
cacheTTL: number;
|
|
31
|
+
}>>> & Readonly<{
|
|
32
|
+
"onDay-click"?: ((data: {
|
|
33
|
+
date: string;
|
|
34
|
+
count: number;
|
|
35
|
+
}) => any) | undefined;
|
|
36
|
+
"onColor-scheme-change"?: ((scheme: ColorScheme) => any) | undefined;
|
|
37
|
+
}>, {
|
|
38
|
+
dataUrl: string;
|
|
39
|
+
cacheTTL: number;
|
|
40
|
+
profileIndex: number;
|
|
41
|
+
colorScheme: ColorScheme;
|
|
42
|
+
showSettings: boolean;
|
|
43
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
44
|
+
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
|
|
45
|
+
export default _default;
|
|
46
|
+
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
47
|
+
type __VLS_TypePropsToRuntimeProps<T> = {
|
|
48
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
49
|
+
type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
50
|
+
} : {
|
|
51
|
+
type: import('vue').PropType<T[K]>;
|
|
52
|
+
required: true;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
type __VLS_WithDefaults<P, D> = {
|
|
56
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
|
|
57
|
+
default: D[K];
|
|
58
|
+
}> : P[K];
|
|
59
|
+
};
|
|
60
|
+
type __VLS_Prettify<T> = {
|
|
61
|
+
[K in keyof T]: T[K];
|
|
62
|
+
} & {};
|
|
63
|
+
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
64
|
+
new (): {
|
|
65
|
+
$slots: S;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ExperienceEntry, CustomStatCalculator } from '../../../core/src';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
dataUrl?: string;
|
|
5
|
+
profileIndexes?: number[];
|
|
6
|
+
experienceData?: ExperienceEntry[];
|
|
7
|
+
showCustomStat?: boolean;
|
|
8
|
+
customStatCalculator?: CustomStatCalculator | null;
|
|
9
|
+
cacheTTL?: number;
|
|
10
|
+
}
|
|
11
|
+
declare function __VLS_template(): {
|
|
12
|
+
"icon-experience"?(_: {}): any;
|
|
13
|
+
"icon-projects"?(_: {}): any;
|
|
14
|
+
"icon-commits"?(_: {}): any;
|
|
15
|
+
"icon-custom"?(_: {}): any;
|
|
16
|
+
"custom-stat-label"?(_: {}): any;
|
|
17
|
+
};
|
|
18
|
+
declare const __VLS_component: import('vue').DefineComponent<import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<Props>, {
|
|
19
|
+
dataUrl: string;
|
|
20
|
+
profileIndexes: () => never[];
|
|
21
|
+
experienceData: () => never[];
|
|
22
|
+
showCustomStat: boolean;
|
|
23
|
+
customStatCalculator: null;
|
|
24
|
+
cacheTTL: number;
|
|
25
|
+
}>>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<Props>, {
|
|
26
|
+
dataUrl: string;
|
|
27
|
+
profileIndexes: () => never[];
|
|
28
|
+
experienceData: () => never[];
|
|
29
|
+
showCustomStat: boolean;
|
|
30
|
+
customStatCalculator: null;
|
|
31
|
+
cacheTTL: number;
|
|
32
|
+
}>>> & Readonly<{}>, {
|
|
33
|
+
dataUrl: string;
|
|
34
|
+
cacheTTL: number;
|
|
35
|
+
profileIndexes: number[];
|
|
36
|
+
experienceData: ExperienceEntry[];
|
|
37
|
+
showCustomStat: boolean;
|
|
38
|
+
customStatCalculator: CustomStatCalculator | null;
|
|
39
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
40
|
+
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
|
|
41
|
+
export default _default;
|
|
42
|
+
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
43
|
+
type __VLS_TypePropsToRuntimeProps<T> = {
|
|
44
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
45
|
+
type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
46
|
+
} : {
|
|
47
|
+
type: import('vue').PropType<T[K]>;
|
|
48
|
+
required: true;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
type __VLS_WithDefaults<P, D> = {
|
|
52
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
|
|
53
|
+
default: D[K];
|
|
54
|
+
}> : P[K];
|
|
55
|
+
};
|
|
56
|
+
type __VLS_Prettify<T> = {
|
|
57
|
+
[K in keyof T]: T[K];
|
|
58
|
+
} & {};
|
|
59
|
+
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
60
|
+
new (): {
|
|
61
|
+
$slots: S;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { GitStatsData, DataSource } from '../../../core/src';
|
|
2
|
+
|
|
3
|
+
export interface UseGitStatsConfig {
|
|
4
|
+
dataUrl?: string;
|
|
5
|
+
cacheTTL?: number;
|
|
6
|
+
useStaleCache?: boolean;
|
|
7
|
+
cacheKey?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function useGitStats(config?: UseGitStatsConfig): {
|
|
10
|
+
data: import('vue').Ref<{
|
|
11
|
+
lastUpdated: string;
|
|
12
|
+
profiles: {
|
|
13
|
+
username: string;
|
|
14
|
+
platform: import('../../../core/src').Platform;
|
|
15
|
+
stats: {
|
|
16
|
+
projectCount: number;
|
|
17
|
+
commitCount: number;
|
|
18
|
+
contributions?: {
|
|
19
|
+
firstDay: string;
|
|
20
|
+
contributionDays: {
|
|
21
|
+
date: string;
|
|
22
|
+
contributionCount: number;
|
|
23
|
+
weekday: number;
|
|
24
|
+
}[];
|
|
25
|
+
}[] | undefined;
|
|
26
|
+
};
|
|
27
|
+
}[];
|
|
28
|
+
totals: {
|
|
29
|
+
projectCount: number;
|
|
30
|
+
commitCount: number;
|
|
31
|
+
};
|
|
32
|
+
metadata: {
|
|
33
|
+
fetchedAt: number;
|
|
34
|
+
source: string;
|
|
35
|
+
isDummy?: boolean | undefined;
|
|
36
|
+
};
|
|
37
|
+
cachedAt?: number | undefined;
|
|
38
|
+
} | null, GitStatsData | {
|
|
39
|
+
lastUpdated: string;
|
|
40
|
+
profiles: {
|
|
41
|
+
username: string;
|
|
42
|
+
platform: import('../../../core/src').Platform;
|
|
43
|
+
stats: {
|
|
44
|
+
projectCount: number;
|
|
45
|
+
commitCount: number;
|
|
46
|
+
contributions?: {
|
|
47
|
+
firstDay: string;
|
|
48
|
+
contributionDays: {
|
|
49
|
+
date: string;
|
|
50
|
+
contributionCount: number;
|
|
51
|
+
weekday: number;
|
|
52
|
+
}[];
|
|
53
|
+
}[] | undefined;
|
|
54
|
+
};
|
|
55
|
+
}[];
|
|
56
|
+
totals: {
|
|
57
|
+
projectCount: number;
|
|
58
|
+
commitCount: number;
|
|
59
|
+
};
|
|
60
|
+
metadata: {
|
|
61
|
+
fetchedAt: number;
|
|
62
|
+
source: string;
|
|
63
|
+
isDummy?: boolean | undefined;
|
|
64
|
+
};
|
|
65
|
+
cachedAt?: number | undefined;
|
|
66
|
+
} | null>;
|
|
67
|
+
loading: import('vue').Ref<boolean, boolean>;
|
|
68
|
+
error: import('vue').Ref<Error | null, Error | null>;
|
|
69
|
+
dataSource: import('vue').Ref<DataSource | null, DataSource | null>;
|
|
70
|
+
dataSourceText: import('vue').ComputedRef<"" | "⚠️ Using dummy data for testing" | "Real-time data" | "Cached data" | "Sample data">;
|
|
71
|
+
lastUpdatedText: import('vue').ComputedRef<string>;
|
|
72
|
+
isDummy: import('vue').Ref<boolean, boolean>;
|
|
73
|
+
loadData: () => Promise<GitStatsData | null>;
|
|
74
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { App } from 'vue';
|
|
2
|
+
import { default as ContributionGraph } from './components/ContributionGraph.vue';
|
|
3
|
+
import { default as StatsBreakdown } from './components/StatsBreakdown.vue';
|
|
4
|
+
import { useGitStats } from './composables/useGitStats';
|
|
5
|
+
|
|
6
|
+
export * from '../../core/src';
|
|
7
|
+
export { ContributionGraph, StatsBreakdown, useGitStats };
|
|
8
|
+
export interface VueGitStatsPlugin {
|
|
9
|
+
install: (app: App) => void;
|
|
10
|
+
}
|
|
11
|
+
declare const VueGitStats: VueGitStatsPlugin;
|
|
12
|
+
export default VueGitStats;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Ref, ComputedRef } from 'vue';
|
|
2
|
+
import { GitStatsData, DataSource } from '../../core/src';
|
|
3
|
+
|
|
4
|
+
export interface UseGitStatsReturn {
|
|
5
|
+
data: Ref<GitStatsData | null>;
|
|
6
|
+
loading: Ref<boolean>;
|
|
7
|
+
error: Ref<Error | null>;
|
|
8
|
+
dataSource: Ref<DataSource | null>;
|
|
9
|
+
dataSourceText: ComputedRef<string>;
|
|
10
|
+
lastUpdatedText: ComputedRef<string>;
|
|
11
|
+
isDummy: Ref<boolean>;
|
|
12
|
+
loadData: () => Promise<GitStatsData | null>;
|
|
13
|
+
cacheData: (data: GitStatsData) => void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
var nt = Object.defineProperty, rt = Object.defineProperties;
|
|
2
|
+
var it = Object.getOwnPropertyDescriptors;
|
|
3
|
+
var P = Object.getOwnPropertySymbols;
|
|
4
|
+
var lt = Object.prototype.hasOwnProperty, ct = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var V = (e, t, a) => t in e ? nt(e, t, { enumerable: !0, configurable: !0, writable: !0, value: a }) : e[t] = a, R = (e, t) => {
|
|
6
|
+
for (var a in t || (t = {}))
|
|
7
|
+
lt.call(t, a) && V(e, a, t[a]);
|
|
8
|
+
if (P)
|
|
9
|
+
for (var a of P(t))
|
|
10
|
+
ct.call(t, a) && V(e, a, t[a]);
|
|
11
|
+
return e;
|
|
12
|
+
}, Y = (e, t) => rt(e, it(t));
|
|
13
|
+
var M = (e, t, a) => new Promise((i, l) => {
|
|
14
|
+
var f = (u) => {
|
|
15
|
+
try {
|
|
16
|
+
o(a.next(u));
|
|
17
|
+
} catch (b) {
|
|
18
|
+
l(b);
|
|
19
|
+
}
|
|
20
|
+
}, d = (u) => {
|
|
21
|
+
try {
|
|
22
|
+
o(a.throw(u));
|
|
23
|
+
} catch (b) {
|
|
24
|
+
l(b);
|
|
25
|
+
}
|
|
26
|
+
}, o = (u) => u.done ? i(u.value) : Promise.resolve(u.value).then(f, d);
|
|
27
|
+
o((a = a.apply(e, t)).next());
|
|
28
|
+
});
|
|
29
|
+
import { ref as S, computed as I, defineComponent as q, watch as z, createElementBlock as y, openBlock as h, createElementVNode as r, createCommentVNode as L, toDisplayString as D, normalizeClass as W, unref as k, renderSlot as j, createTextVNode as C, Fragment as O, renderList as A, normalizeStyle as dt, createStaticVNode as J } from "vue";
|
|
30
|
+
function de(e) {
|
|
31
|
+
return M(this, null, function* () {
|
|
32
|
+
const t = yield fetch(e);
|
|
33
|
+
if (!t.ok)
|
|
34
|
+
throw new Error(`Failed to fetch git stats: ${t.statusText}`);
|
|
35
|
+
return t.json();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function ut() {
|
|
39
|
+
const e = [], t = /* @__PURE__ */ new Date(), a = new Date(t);
|
|
40
|
+
a.setDate(a.getDate() - a.getDay());
|
|
41
|
+
const i = new Date(a);
|
|
42
|
+
i.setDate(i.getDate() - 52 * 7);
|
|
43
|
+
const l = new Date(i);
|
|
44
|
+
for (let f = 0; f < 53; f++) {
|
|
45
|
+
const d = {
|
|
46
|
+
weekStart: new Date(l).toISOString().split("T")[0],
|
|
47
|
+
contributionDays: []
|
|
48
|
+
};
|
|
49
|
+
for (let o = 0; o < 7; o++) {
|
|
50
|
+
const u = l > t, b = o === 0 || o === 6;
|
|
51
|
+
let m = 0;
|
|
52
|
+
u || (b ? m = Math.random() < 0.3 ? Math.floor(Math.random() * 5) : 0 : m = Math.floor(Math.random() * 15)), d.contributionDays.push({
|
|
53
|
+
date: new Date(l).toISOString().split("T")[0],
|
|
54
|
+
contributionCount: m,
|
|
55
|
+
weekday: o
|
|
56
|
+
}), l.setDate(l.getDate() + 1);
|
|
57
|
+
}
|
|
58
|
+
e.push(d);
|
|
59
|
+
}
|
|
60
|
+
return e;
|
|
61
|
+
}
|
|
62
|
+
function G(e = {}) {
|
|
63
|
+
const {
|
|
64
|
+
username: t = "demo-user",
|
|
65
|
+
platform: a = "github",
|
|
66
|
+
projectCount: i = 30,
|
|
67
|
+
commitCount: l = 2500
|
|
68
|
+
} = e, f = ut();
|
|
69
|
+
return f.reduce((d, o) => d + o.contributionDays.reduce(
|
|
70
|
+
(u, b) => u + b.contributionCount,
|
|
71
|
+
0
|
|
72
|
+
), 0), {
|
|
73
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
74
|
+
profiles: [
|
|
75
|
+
{
|
|
76
|
+
username: t,
|
|
77
|
+
platform: a,
|
|
78
|
+
stats: {
|
|
79
|
+
projectCount: i,
|
|
80
|
+
commitCount: l,
|
|
81
|
+
contributions: f.map((d) => ({
|
|
82
|
+
firstDay: d.weekStart,
|
|
83
|
+
contributionDays: d.contributionDays
|
|
84
|
+
}))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
totals: {
|
|
89
|
+
projectCount: i,
|
|
90
|
+
commitCount: l
|
|
91
|
+
},
|
|
92
|
+
metadata: {
|
|
93
|
+
fetchedAt: Date.now(),
|
|
94
|
+
source: "dummy_data",
|
|
95
|
+
isDummy: !0
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function ue() {
|
|
100
|
+
const e = G({
|
|
101
|
+
username: "demo-github",
|
|
102
|
+
platform: "github",
|
|
103
|
+
projectCount: 45,
|
|
104
|
+
commitCount: 2847
|
|
105
|
+
}), t = G({
|
|
106
|
+
username: "demo-gitlab",
|
|
107
|
+
platform: "gitlab",
|
|
108
|
+
projectCount: 7,
|
|
109
|
+
commitCount: 523
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
113
|
+
profiles: [e.profiles[0], t.profiles[0]],
|
|
114
|
+
totals: {
|
|
115
|
+
projectCount: 52,
|
|
116
|
+
commitCount: 3370
|
|
117
|
+
},
|
|
118
|
+
metadata: {
|
|
119
|
+
fetchedAt: Date.now(),
|
|
120
|
+
source: "dummy_data",
|
|
121
|
+
isDummy: !0
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function fe(e = "dummy-git-stats.json") {
|
|
126
|
+
const t = G(), a = JSON.stringify(t, null, " ");
|
|
127
|
+
if (typeof window != "undefined") {
|
|
128
|
+
const i = new Blob([a], { type: "application/json" }), l = URL.createObjectURL(i), f = document.createElement("a");
|
|
129
|
+
f.href = l, f.download = e, f.click(), URL.revokeObjectURL(l);
|
|
130
|
+
} else
|
|
131
|
+
try {
|
|
132
|
+
require("fs").writeFileSync(e, a), console.log(`✓ Dummy data saved to ${e}`);
|
|
133
|
+
} catch (i) {
|
|
134
|
+
console.error("Failed to save dummy data:", i);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function ft(e) {
|
|
138
|
+
return M(this, null, function* () {
|
|
139
|
+
var f, d;
|
|
140
|
+
const { dataUrl: t, cacheKey: a = "git_stats_cache", useStaleCache: i = !0 } = e;
|
|
141
|
+
try {
|
|
142
|
+
const o = yield fetch(t);
|
|
143
|
+
if (o.ok) {
|
|
144
|
+
const u = yield o.json();
|
|
145
|
+
return typeof window != "undefined" && localStorage.setItem(
|
|
146
|
+
a,
|
|
147
|
+
JSON.stringify(Y(R({}, u), {
|
|
148
|
+
cachedAt: Date.now()
|
|
149
|
+
}))
|
|
150
|
+
), {
|
|
151
|
+
data: u,
|
|
152
|
+
error: null,
|
|
153
|
+
source: "static",
|
|
154
|
+
isDummy: ((f = u.metadata) == null ? void 0 : f.isDummy) === !0
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
} catch (o) {
|
|
158
|
+
console.warn("Failed to fetch from static file:", o);
|
|
159
|
+
}
|
|
160
|
+
if (i && typeof window != "undefined")
|
|
161
|
+
try {
|
|
162
|
+
const o = localStorage.getItem(a);
|
|
163
|
+
if (o) {
|
|
164
|
+
const u = JSON.parse(o);
|
|
165
|
+
return {
|
|
166
|
+
data: u,
|
|
167
|
+
error: null,
|
|
168
|
+
source: "cache",
|
|
169
|
+
isDummy: ((d = u.metadata) == null ? void 0 : d.isDummy) === !0
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
} catch (o) {
|
|
173
|
+
console.warn("Failed to load from cache:", o);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
data: yt(),
|
|
177
|
+
error: null,
|
|
178
|
+
source: "mock",
|
|
179
|
+
isDummy: !1
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function mt(e) {
|
|
184
|
+
const t = new Date(e), a = /* @__PURE__ */ new Date(), i = Math.floor((a.getTime() - t.getTime()) / (1e3 * 60 * 60));
|
|
185
|
+
if (i < 1) return "just now";
|
|
186
|
+
if (i < 24) return `${i} hours ago`;
|
|
187
|
+
const l = Math.floor(i / 24);
|
|
188
|
+
return l === 1 ? "yesterday" : l < 7 ? `${l} days ago` : t.toLocaleDateString("en-US", {
|
|
189
|
+
month: "short",
|
|
190
|
+
day: "numeric",
|
|
191
|
+
year: t.getFullYear() !== a.getFullYear() ? "numeric" : void 0
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function me(e) {
|
|
195
|
+
return e === 0 ? 0 : e <= 3 ? 1 : e <= 6 ? 2 : e <= 9 ? 3 : 4;
|
|
196
|
+
}
|
|
197
|
+
function vt(e) {
|
|
198
|
+
if (e.length === 0) return 0;
|
|
199
|
+
const t = {};
|
|
200
|
+
return e.forEach((a) => {
|
|
201
|
+
var d;
|
|
202
|
+
const i = a.endDate ? new Date(a.endDate) : /* @__PURE__ */ new Date(), l = new Date(a.startDate), f = (i.getTime() - l.getTime()) / (1e3 * 60 * 60 * 24 * 365.25);
|
|
203
|
+
(d = a.skills) == null || d.forEach((o) => {
|
|
204
|
+
t[o] || (t[o] = 0), t[o] += f;
|
|
205
|
+
});
|
|
206
|
+
}), Math.max(...Object.values(t), 0);
|
|
207
|
+
}
|
|
208
|
+
function yt() {
|
|
209
|
+
return {
|
|
210
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
211
|
+
profiles: [
|
|
212
|
+
{
|
|
213
|
+
username: "mockuser",
|
|
214
|
+
platform: "github",
|
|
215
|
+
stats: {
|
|
216
|
+
projectCount: 30,
|
|
217
|
+
commitCount: 2500,
|
|
218
|
+
contributions: []
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
totals: {
|
|
223
|
+
projectCount: 30,
|
|
224
|
+
commitCount: 2500
|
|
225
|
+
},
|
|
226
|
+
metadata: {
|
|
227
|
+
source: "mock",
|
|
228
|
+
fetchedAt: Date.now()
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function K(e = {}) {
|
|
233
|
+
const {
|
|
234
|
+
dataUrl: t = "/data/git-stats.json",
|
|
235
|
+
cacheTTL: a = 24 * 60 * 60 * 1e3,
|
|
236
|
+
useStaleCache: i = !0,
|
|
237
|
+
cacheKey: l = "git_stats_cache"
|
|
238
|
+
} = e, f = S(!1), d = S(null), o = S(null), u = S(null), b = S(!1);
|
|
239
|
+
function m() {
|
|
240
|
+
return M(this, null, function* () {
|
|
241
|
+
f.value = !0, d.value = null;
|
|
242
|
+
try {
|
|
243
|
+
const g = yield ft({
|
|
244
|
+
dataUrl: t,
|
|
245
|
+
cacheTTL: a,
|
|
246
|
+
cacheKey: l,
|
|
247
|
+
useStaleCache: i
|
|
248
|
+
});
|
|
249
|
+
return o.value = g.data, d.value = g.error, u.value = g.source, b.value = g.isDummy, g.data;
|
|
250
|
+
} catch (g) {
|
|
251
|
+
return d.value = g instanceof Error ? g : new Error("Failed to load data"), null;
|
|
252
|
+
} finally {
|
|
253
|
+
f.value = !1;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const c = I(() => {
|
|
258
|
+
var g;
|
|
259
|
+
return (g = o.value) != null && g.lastUpdated ? mt(o.value.lastUpdated) : "";
|
|
260
|
+
}), _ = I(() => {
|
|
261
|
+
if (b.value)
|
|
262
|
+
return "⚠️ Using dummy data for testing";
|
|
263
|
+
switch (u.value) {
|
|
264
|
+
case "static":
|
|
265
|
+
return "Real-time data";
|
|
266
|
+
case "cache":
|
|
267
|
+
return "Cached data";
|
|
268
|
+
case "mock":
|
|
269
|
+
return "Sample data";
|
|
270
|
+
default:
|
|
271
|
+
return "";
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
return m(), {
|
|
275
|
+
data: o,
|
|
276
|
+
loading: f,
|
|
277
|
+
error: d,
|
|
278
|
+
dataSource: u,
|
|
279
|
+
dataSourceText: _,
|
|
280
|
+
lastUpdatedText: c,
|
|
281
|
+
isDummy: b,
|
|
282
|
+
loadData: m
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const ht = { class: "git-contribution-graph" }, pt = { class: "graph-header" }, gt = { class: "header-info" }, bt = { class: "contribution-count" }, _t = {
|
|
286
|
+
key: 0,
|
|
287
|
+
class: "header-actions"
|
|
288
|
+
}, kt = {
|
|
289
|
+
key: 0,
|
|
290
|
+
class: "settings-dropdown"
|
|
291
|
+
}, Dt = ["onClick"], St = {
|
|
292
|
+
key: 0,
|
|
293
|
+
class: "loading-state"
|
|
294
|
+
}, wt = {
|
|
295
|
+
key: 1,
|
|
296
|
+
class: "graph-container"
|
|
297
|
+
}, Ct = { class: "graph-content-wrapper" }, $t = { class: "months-row" }, Tt = { class: "months-container" }, jt = { class: "grid-container" }, Lt = { class: "contribution-grid" }, Ut = ["title", "onClick"], xt = { class: "graph-footer" }, Ft = {
|
|
298
|
+
key: 0,
|
|
299
|
+
class: "last-updated"
|
|
300
|
+
}, It = /* @__PURE__ */ q({
|
|
301
|
+
__name: "ContributionGraph",
|
|
302
|
+
props: {
|
|
303
|
+
dataUrl: { default: "/data/git-stats.json" },
|
|
304
|
+
profileIndex: { default: 0 },
|
|
305
|
+
colorScheme: { default: "green" },
|
|
306
|
+
showSettings: { type: Boolean, default: !0 },
|
|
307
|
+
cacheTTL: { default: 24 * 60 * 60 * 1e3 }
|
|
308
|
+
},
|
|
309
|
+
emits: ["day-click", "color-scheme-change"],
|
|
310
|
+
setup(e, { emit: t }) {
|
|
311
|
+
const a = e, i = t, { data: l, loading: f, dataSourceText: d, lastUpdatedText: o, isDummy: u } = K(
|
|
312
|
+
{
|
|
313
|
+
dataUrl: a.dataUrl,
|
|
314
|
+
cacheTTL: a.cacheTTL
|
|
315
|
+
}
|
|
316
|
+
), b = S(a.colorScheme), m = S(!1), c = ["green", "blue", "purple", "orange"], _ = S([]), g = S([]);
|
|
317
|
+
z(
|
|
318
|
+
l,
|
|
319
|
+
(s) => {
|
|
320
|
+
var n, v, p;
|
|
321
|
+
if ((p = (v = (n = s == null ? void 0 : s.profiles) == null ? void 0 : n[a.profileIndex]) == null ? void 0 : v.stats) != null && p.contributions) {
|
|
322
|
+
const w = s.profiles[a.profileIndex].stats.contributions;
|
|
323
|
+
w && (_.value = B(w), Q());
|
|
324
|
+
} else
|
|
325
|
+
_.value = [];
|
|
326
|
+
},
|
|
327
|
+
{ immediate: !0 }
|
|
328
|
+
);
|
|
329
|
+
const T = I(() => !_.value || _.value.length === 0 ? 0 : _.value.reduce((s, n) => !n.days || !Array.isArray(n.days) ? s : s + n.days.reduce((v, p) => v + (p.count || 0), 0), 0));
|
|
330
|
+
function B(s) {
|
|
331
|
+
if (!s || !Array.isArray(s))
|
|
332
|
+
return $();
|
|
333
|
+
const n = s.map((v) => ({
|
|
334
|
+
weekStart: v.firstDay || "",
|
|
335
|
+
days: v.contributionDays.map((p) => {
|
|
336
|
+
var w;
|
|
337
|
+
return {
|
|
338
|
+
date: p.date || "",
|
|
339
|
+
count: (w = p.contributionCount) != null ? w : 0,
|
|
340
|
+
weekday: p.weekday || 0
|
|
341
|
+
};
|
|
342
|
+
})
|
|
343
|
+
}));
|
|
344
|
+
for (; n.length < 53; )
|
|
345
|
+
n.push(U());
|
|
346
|
+
return n;
|
|
347
|
+
}
|
|
348
|
+
function $() {
|
|
349
|
+
const s = [];
|
|
350
|
+
for (let n = 0; n < 53; n++)
|
|
351
|
+
s.push(U());
|
|
352
|
+
return s;
|
|
353
|
+
}
|
|
354
|
+
function U() {
|
|
355
|
+
const s = [];
|
|
356
|
+
for (let n = 0; n < 7; n++)
|
|
357
|
+
s.push({ date: "", count: 0, weekday: n });
|
|
358
|
+
return { weekStart: "", days: s };
|
|
359
|
+
}
|
|
360
|
+
function Q() {
|
|
361
|
+
if (!_.value || _.value.length === 0) {
|
|
362
|
+
g.value = [];
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const s = [];
|
|
366
|
+
let n = -1, v = -1;
|
|
367
|
+
_.value.forEach((p, w) => {
|
|
368
|
+
if (!p.days || p.days.length === 0) return;
|
|
369
|
+
const E = p.days[0].date;
|
|
370
|
+
if (!E) return;
|
|
371
|
+
const N = E.split("-");
|
|
372
|
+
if (N.length !== 3) return;
|
|
373
|
+
const [x, F] = N.map(Number);
|
|
374
|
+
if (!(isNaN(x) || isNaN(F)) && (F !== n || x !== v)) {
|
|
375
|
+
const ot = new Date(x, F - 1, 1);
|
|
376
|
+
s.push({
|
|
377
|
+
week: w,
|
|
378
|
+
month: F - 1,
|
|
379
|
+
year: x,
|
|
380
|
+
label: ot.toLocaleDateString("en-US", { month: "short" })
|
|
381
|
+
}), n = F, v = x;
|
|
382
|
+
}
|
|
383
|
+
}), g.value = s;
|
|
384
|
+
}
|
|
385
|
+
function X(s) {
|
|
386
|
+
return `level-${Z(s)} ${b.value}`;
|
|
387
|
+
}
|
|
388
|
+
function Z(s) {
|
|
389
|
+
return s === 0 ? 0 : s <= 3 ? 1 : s <= 6 ? 2 : s <= 9 ? 3 : 4;
|
|
390
|
+
}
|
|
391
|
+
function tt(s) {
|
|
392
|
+
if (!s.date) return "";
|
|
393
|
+
const [n, v, p] = s.date.split("-").map(Number), E = new Date(n, v - 1, p).toLocaleDateString("en-US", {
|
|
394
|
+
weekday: "short",
|
|
395
|
+
year: "numeric",
|
|
396
|
+
month: "short",
|
|
397
|
+
day: "numeric"
|
|
398
|
+
}), N = s.count === 1 ? "contribution" : "contributions";
|
|
399
|
+
return `${s.count} ${N} on ${E}`;
|
|
400
|
+
}
|
|
401
|
+
function et(s) {
|
|
402
|
+
i("day-click", { date: s.date, count: s.count });
|
|
403
|
+
}
|
|
404
|
+
function at() {
|
|
405
|
+
m.value = !m.value;
|
|
406
|
+
}
|
|
407
|
+
function st(s) {
|
|
408
|
+
b.value = s, m.value = !1, i("color-scheme-change", s);
|
|
409
|
+
}
|
|
410
|
+
return (s, n) => (h(), y("div", ht, [
|
|
411
|
+
r("div", pt, [
|
|
412
|
+
r("div", gt, [
|
|
413
|
+
r("h5", bt, D(T.value.toLocaleString()) + " contributions in the last year ", 1),
|
|
414
|
+
r("small", {
|
|
415
|
+
class: W(["data-source-text", { "is-dummy": k(u) }])
|
|
416
|
+
}, D(k(d)), 3)
|
|
417
|
+
]),
|
|
418
|
+
e.showSettings ? (h(), y("div", _t, [
|
|
419
|
+
r("button", {
|
|
420
|
+
class: "settings-btn",
|
|
421
|
+
type: "button",
|
|
422
|
+
onClick: at
|
|
423
|
+
}, [
|
|
424
|
+
j(s.$slots, "settings-icon", {}, () => [
|
|
425
|
+
n[0] || (n[0] = C("⚙️", -1))
|
|
426
|
+
], !0),
|
|
427
|
+
n[1] || (n[1] = C(" Settings ", -1))
|
|
428
|
+
]),
|
|
429
|
+
m.value ? (h(), y("div", kt, [
|
|
430
|
+
(h(), y(O, null, A(c, (v) => r("button", {
|
|
431
|
+
key: v,
|
|
432
|
+
onClick: (p) => st(v),
|
|
433
|
+
class: "settings-item"
|
|
434
|
+
}, D(v) + " theme ", 9, Dt)), 64))
|
|
435
|
+
])) : L("", !0)
|
|
436
|
+
])) : L("", !0)
|
|
437
|
+
]),
|
|
438
|
+
k(f) ? (h(), y("div", St, [...n[2] || (n[2] = [
|
|
439
|
+
r("div", { class: "spinner" }, null, -1),
|
|
440
|
+
r("span", null, "Loading contributions...", -1)
|
|
441
|
+
])])) : (h(), y("div", wt, [
|
|
442
|
+
r("div", Ct, [
|
|
443
|
+
r("div", $t, [
|
|
444
|
+
n[3] || (n[3] = r("div", { class: "month-spacer" }, null, -1)),
|
|
445
|
+
r("div", Tt, [
|
|
446
|
+
(h(!0), y(O, null, A(g.value, (v) => (h(), y("div", {
|
|
447
|
+
key: `${v.year}-${v.month}`,
|
|
448
|
+
class: "month-label",
|
|
449
|
+
style: dt({
|
|
450
|
+
gridColumn: `${v.week + 1} / span 1`
|
|
451
|
+
})
|
|
452
|
+
}, D(v.label), 5))), 128))
|
|
453
|
+
])
|
|
454
|
+
]),
|
|
455
|
+
r("div", jt, [
|
|
456
|
+
n[4] || (n[4] = J('<div class="day-labels" data-v-f1ff100d><div class="day-label" data-v-f1ff100d>Mon</div><div class="day-label" data-v-f1ff100d></div><div class="day-label" data-v-f1ff100d>Wed</div><div class="day-label" data-v-f1ff100d></div><div class="day-label" data-v-f1ff100d>Fri</div><div class="day-label" data-v-f1ff100d></div><div class="day-label" data-v-f1ff100d></div></div>', 1)),
|
|
457
|
+
r("div", Lt, [
|
|
458
|
+
(h(!0), y(O, null, A(_.value, (v) => (h(), y("div", {
|
|
459
|
+
key: v.weekStart,
|
|
460
|
+
class: "contribution-week"
|
|
461
|
+
}, [
|
|
462
|
+
(h(!0), y(O, null, A(v.days, (p) => (h(), y("div", {
|
|
463
|
+
key: p.date,
|
|
464
|
+
class: W(["contribution-day", X(p.count)]),
|
|
465
|
+
title: tt(p),
|
|
466
|
+
onClick: (w) => et(p)
|
|
467
|
+
}, null, 10, Ut))), 128))
|
|
468
|
+
]))), 128))
|
|
469
|
+
])
|
|
470
|
+
])
|
|
471
|
+
]),
|
|
472
|
+
r("div", xt, [
|
|
473
|
+
k(o) ? (h(), y("small", Ft, " Last updated: " + D(k(o)), 1)) : L("", !0),
|
|
474
|
+
n[5] || (n[5] = J('<div class="legend" data-v-f1ff100d><small class="legend-label" data-v-f1ff100d>Less</small><div class="legend-squares" data-v-f1ff100d><div class="contribution-day level-0" data-v-f1ff100d></div><div class="contribution-day level-1" data-v-f1ff100d></div><div class="contribution-day level-2" data-v-f1ff100d></div><div class="contribution-day level-3" data-v-f1ff100d></div><div class="contribution-day level-4" data-v-f1ff100d></div></div><small class="legend-label" data-v-f1ff100d>More</small></div>', 1))
|
|
475
|
+
])
|
|
476
|
+
]))
|
|
477
|
+
]));
|
|
478
|
+
}
|
|
479
|
+
}), H = (e, t) => {
|
|
480
|
+
const a = e.__vccOpts || e;
|
|
481
|
+
for (const [i, l] of t)
|
|
482
|
+
a[i] = l;
|
|
483
|
+
return a;
|
|
484
|
+
}, Et = /* @__PURE__ */ H(It, [["__scopeId", "data-v-f1ff100d"]]), Nt = { class: "git-stats-breakdown" }, Mt = { class: "stats-grid" }, Ot = { class: "stat-card" }, At = { class: "stat-icon" }, Bt = { class: "stat-content" }, Gt = { class: "stat-value" }, Pt = { class: "stat-card" }, Vt = { class: "stat-icon" }, Rt = { class: "stat-content" }, Yt = {
|
|
485
|
+
key: 0,
|
|
486
|
+
class: "stat-loading"
|
|
487
|
+
}, Wt = {
|
|
488
|
+
key: 1,
|
|
489
|
+
class: "stat-value"
|
|
490
|
+
}, Jt = { class: "stat-card" }, qt = { class: "stat-icon" }, zt = { class: "stat-content" }, Kt = {
|
|
491
|
+
key: 0,
|
|
492
|
+
class: "stat-loading"
|
|
493
|
+
}, Ht = {
|
|
494
|
+
key: 1,
|
|
495
|
+
class: "stat-value"
|
|
496
|
+
}, Qt = {
|
|
497
|
+
key: 0,
|
|
498
|
+
class: "stat-card"
|
|
499
|
+
}, Xt = { class: "stat-icon" }, Zt = { class: "stat-content" }, te = {
|
|
500
|
+
key: 0,
|
|
501
|
+
class: "stat-loading"
|
|
502
|
+
}, ee = {
|
|
503
|
+
key: 1,
|
|
504
|
+
class: "stat-value"
|
|
505
|
+
}, ae = { class: "stat-label" }, se = { class: "stats-footer" }, oe = {
|
|
506
|
+
key: 0,
|
|
507
|
+
class: "data-source"
|
|
508
|
+
}, ne = { key: 0 }, re = /* @__PURE__ */ q({
|
|
509
|
+
__name: "StatsBreakdown",
|
|
510
|
+
props: {
|
|
511
|
+
dataUrl: { default: "/data/git-stats.json" },
|
|
512
|
+
profileIndexes: { default: () => [] },
|
|
513
|
+
experienceData: { default: () => [] },
|
|
514
|
+
showCustomStat: { type: Boolean, default: !0 },
|
|
515
|
+
customStatCalculator: { type: [Function, null], default: null },
|
|
516
|
+
cacheTTL: { default: 24 * 60 * 60 * 1e3 }
|
|
517
|
+
},
|
|
518
|
+
setup(e) {
|
|
519
|
+
const t = e, { data: a, loading: i, dataSourceText: l, lastUpdatedText: f } = K({
|
|
520
|
+
dataUrl: t.dataUrl,
|
|
521
|
+
cacheTTL: t.cacheTTL
|
|
522
|
+
}), d = S(0), o = S(0);
|
|
523
|
+
z(
|
|
524
|
+
a,
|
|
525
|
+
(m) => {
|
|
526
|
+
var c, _;
|
|
527
|
+
if (m)
|
|
528
|
+
if (t.profileIndexes.length > 0) {
|
|
529
|
+
let g = 0, T = 0;
|
|
530
|
+
t.profileIndexes.forEach((B) => {
|
|
531
|
+
var U;
|
|
532
|
+
const $ = (U = m.profiles) == null ? void 0 : U[B];
|
|
533
|
+
$ != null && $.stats && (g += $.stats.projectCount || 0, T += $.stats.commitCount || 0);
|
|
534
|
+
}), d.value = g, o.value = T;
|
|
535
|
+
} else
|
|
536
|
+
d.value = ((c = m.totals) == null ? void 0 : c.projectCount) || 0, o.value = ((_ = m.totals) == null ? void 0 : _.commitCount) || 0;
|
|
537
|
+
},
|
|
538
|
+
{ immediate: !0 }
|
|
539
|
+
);
|
|
540
|
+
const u = I(() => vt(t.experienceData).toFixed(1)), b = I(() => {
|
|
541
|
+
if (t.customStatCalculator) {
|
|
542
|
+
const T = {
|
|
543
|
+
projects: d.value,
|
|
544
|
+
commits: o.value,
|
|
545
|
+
years: parseFloat(u.value)
|
|
546
|
+
};
|
|
547
|
+
return t.customStatCalculator(T);
|
|
548
|
+
}
|
|
549
|
+
return (d.value * 1.5 + o.value * 1.2 + parseFloat(u.value) * 1.5).toFixed(2);
|
|
550
|
+
});
|
|
551
|
+
return (m, c) => (h(), y("div", Nt, [
|
|
552
|
+
r("div", Mt, [
|
|
553
|
+
r("div", Ot, [
|
|
554
|
+
r("div", At, [
|
|
555
|
+
j(m.$slots, "icon-experience", {}, () => [
|
|
556
|
+
c[0] || (c[0] = C("⏱️", -1))
|
|
557
|
+
], !0)
|
|
558
|
+
]),
|
|
559
|
+
r("div", Bt, [
|
|
560
|
+
r("div", Gt, D(u.value), 1),
|
|
561
|
+
c[1] || (c[1] = r("div", { class: "stat-label" }, "Years Experience", -1))
|
|
562
|
+
])
|
|
563
|
+
]),
|
|
564
|
+
r("div", Pt, [
|
|
565
|
+
r("div", Vt, [
|
|
566
|
+
j(m.$slots, "icon-projects", {}, () => [
|
|
567
|
+
c[2] || (c[2] = C("📦", -1))
|
|
568
|
+
], !0)
|
|
569
|
+
]),
|
|
570
|
+
r("div", Rt, [
|
|
571
|
+
k(i) ? (h(), y("div", Yt, [...c[3] || (c[3] = [
|
|
572
|
+
r("div", { class: "spinner" }, null, -1)
|
|
573
|
+
])])) : (h(), y("div", Wt, D(d.value), 1)),
|
|
574
|
+
c[4] || (c[4] = r("div", { class: "stat-label" }, "Projects", -1))
|
|
575
|
+
])
|
|
576
|
+
]),
|
|
577
|
+
r("div", Jt, [
|
|
578
|
+
r("div", qt, [
|
|
579
|
+
j(m.$slots, "icon-commits", {}, () => [
|
|
580
|
+
c[5] || (c[5] = C("💻", -1))
|
|
581
|
+
], !0)
|
|
582
|
+
]),
|
|
583
|
+
r("div", zt, [
|
|
584
|
+
k(i) ? (h(), y("div", Kt, [...c[6] || (c[6] = [
|
|
585
|
+
r("div", { class: "spinner" }, null, -1)
|
|
586
|
+
])])) : (h(), y("div", Ht, D(o.value), 1)),
|
|
587
|
+
c[7] || (c[7] = r("div", { class: "stat-label" }, "Commits", -1))
|
|
588
|
+
])
|
|
589
|
+
]),
|
|
590
|
+
e.showCustomStat ? (h(), y("div", Qt, [
|
|
591
|
+
r("div", Xt, [
|
|
592
|
+
j(m.$slots, "icon-custom", {}, () => [
|
|
593
|
+
c[8] || (c[8] = C("☕", -1))
|
|
594
|
+
], !0)
|
|
595
|
+
]),
|
|
596
|
+
r("div", Zt, [
|
|
597
|
+
k(i) ? (h(), y("div", te, [...c[9] || (c[9] = [
|
|
598
|
+
r("div", { class: "spinner" }, null, -1)
|
|
599
|
+
])])) : (h(), y("div", ee, D(b.value), 1)),
|
|
600
|
+
r("div", ae, [
|
|
601
|
+
j(m.$slots, "custom-stat-label", {}, () => [
|
|
602
|
+
c[10] || (c[10] = C("Coffee Consumed", -1))
|
|
603
|
+
], !0)
|
|
604
|
+
])
|
|
605
|
+
])
|
|
606
|
+
])) : L("", !0)
|
|
607
|
+
]),
|
|
608
|
+
r("div", se, [
|
|
609
|
+
k(l) ? (h(), y("small", oe, [
|
|
610
|
+
C(D(k(l)) + " ", 1),
|
|
611
|
+
k(f) ? (h(), y("span", ne, " · " + D(k(f)), 1)) : L("", !0)
|
|
612
|
+
])) : L("", !0)
|
|
613
|
+
])
|
|
614
|
+
]));
|
|
615
|
+
}
|
|
616
|
+
}), ie = /* @__PURE__ */ H(re, [["__scopeId", "data-v-97689125"]]), ve = {
|
|
617
|
+
install(e) {
|
|
618
|
+
e.component("ContributionGraph", Et), e.component("StatsBreakdown", ie);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
export {
|
|
622
|
+
Et as ContributionGraph,
|
|
623
|
+
ie as StatsBreakdown,
|
|
624
|
+
vt as calculateYearsExperience,
|
|
625
|
+
ve as default,
|
|
626
|
+
ft as fetchGitStats,
|
|
627
|
+
de as fetchGitStatsAPI,
|
|
628
|
+
mt as formatLastUpdated,
|
|
629
|
+
ut as generateDummyContributions,
|
|
630
|
+
G as generateDummyStats,
|
|
631
|
+
ue as generateMultiProfileDummyStats,
|
|
632
|
+
me as getContributionLevel,
|
|
633
|
+
fe as saveDummyDataToFile,
|
|
634
|
+
K as useGitStats
|
|
635
|
+
};
|
|
636
|
+
//# sourceMappingURL=vue-git-stats.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-git-stats.es.js","sources":["../../core/src/api/fetchGitStats.ts","../../core/src/utils/generateDummyData.js","../../core/src/index.ts","../src/composables/useGitStats.ts","../src/components/ContributionGraph.vue","../src/components/StatsBreakdown.vue","../src/index.ts"],"sourcesContent":["import type { GitStatsData } from '../types'\r\n\r\nexport async function fetchGitStats(url: string): Promise<GitStatsData> {\r\n\tconst response = await fetch(url)\r\n\tif (!response.ok) {\r\n\t\tthrow new Error(`Failed to fetch git stats: ${response.statusText}`)\r\n\t}\r\n\treturn response.json()\r\n}\r\n","/**\n * Generate realistic dummy data for testing and development\n */\n\n/**\n * Generate dummy contribution data (53 weeks)\n */\nexport function generateDummyContributions() {\n\tconst weeks = []\n\tconst now = new Date()\n\n\t// Get the Sunday that starts the week containing today\n\tconst endDate = new Date(now)\n\tendDate.setDate(endDate.getDate() - endDate.getDay())\n\n\t// Go back exactly 52 weeks\n\tconst startDate = new Date(endDate)\n\tstartDate.setDate(startDate.getDate() - 52 * 7)\n\n\tconst currentDate = new Date(startDate)\n\n\tfor (let week = 0; week < 53; week++) {\n\t\tconst weekData = {\n\t\t\tweekStart: new Date(currentDate).toISOString().split('T')[0],\n\t\t\tcontributionDays: [],\n\t\t}\n\n\t\tfor (let day = 0; day < 7; day++) {\n\t\t\tconst isInFuture = currentDate > now\n\t\t\tconst isWeekend = day === 0 || day === 6\n\n\t\t\t// More realistic pattern: fewer commits on weekends, none in future\n\t\t\tlet dayCount = 0\n\t\t\tif (!isInFuture) {\n\t\t\t\tif (isWeekend) {\n\t\t\t\t\tdayCount =\n\t\t\t\t\t\tMath.random() < 0.3 ? Math.floor(Math.random() * 5) : 0\n\t\t\t\t} else {\n\t\t\t\t\tdayCount = Math.floor(Math.random() * 15)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tweekData.contributionDays.push({\n\t\t\t\tdate: new Date(currentDate).toISOString().split('T')[0],\n\t\t\t\tcontributionCount: dayCount,\n\t\t\t\tweekday: day,\n\t\t\t})\n\t\t\tcurrentDate.setDate(currentDate.getDate() + 1)\n\t\t}\n\n\t\tweeks.push(weekData)\n\t}\n\n\treturn weeks\n}\n\n/**\n * Generate complete dummy stats data\n */\nexport function generateDummyStats(options = {}) {\n\tconst {\n\t\tusername = 'demo-user',\n\t\tplatform = 'github',\n\t\tprojectCount = 30,\n\t\tcommitCount = 2500,\n\t} = options\n\n\tconst contributions = generateDummyContributions()\n\tconst totalContributions = contributions.reduce((total, week) => {\n\t\treturn (\n\t\t\ttotal +\n\t\t\tweek.contributionDays.reduce(\n\t\t\t\t(sum, day) => sum + day.contributionCount,\n\t\t\t\t0\n\t\t\t)\n\t\t)\n\t}, 0)\n\n\treturn {\n\t\tlastUpdated: new Date().toISOString(),\n\t\tprofiles: [\n\t\t\t{\n\t\t\t\tusername,\n\t\t\t\tplatform,\n\t\t\t\tstats: {\n\t\t\t\t\tprojectCount,\n\t\t\t\t\tcommitCount,\n\t\t\t\t\tcontributions: contributions.map((week) => ({\n\t\t\t\t\t\tfirstDay: week.weekStart,\n\t\t\t\t\t\tcontributionDays: week.contributionDays,\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\ttotals: {\n\t\t\tprojectCount,\n\t\t\tcommitCount,\n\t\t},\n\t\tmetadata: {\n\t\t\tfetchedAt: Date.now(),\n\t\t\tsource: 'dummy_data',\n\t\t\tisDummy: true,\n\t\t},\n\t}\n}\n\n/**\n * Generate multiple profiles dummy data\n */\nexport function generateMultiProfileDummyStats() {\n\tconst githubProfile = generateDummyStats({\n\t\tusername: 'demo-github',\n\t\tplatform: 'github',\n\t\tprojectCount: 45,\n\t\tcommitCount: 2847,\n\t})\n\n\tconst gitlabProfile = generateDummyStats({\n\t\tusername: 'demo-gitlab',\n\t\tplatform: 'gitlab',\n\t\tprojectCount: 7,\n\t\tcommitCount: 523,\n\t})\n\n\treturn {\n\t\tlastUpdated: new Date().toISOString(),\n\t\tprofiles: [githubProfile.profiles[0], gitlabProfile.profiles[0]],\n\t\ttotals: {\n\t\t\tprojectCount: 45 + 7,\n\t\t\tcommitCount: 2847 + 523,\n\t\t},\n\t\tmetadata: {\n\t\t\tfetchedAt: Date.now(),\n\t\t\tsource: 'dummy_data',\n\t\t\tisDummy: true,\n\t\t},\n\t}\n}\n\n/**\n * Save dummy data to a file (for testing)\n */\nexport function saveDummyDataToFile(filepath = 'dummy-git-stats.json') {\n\tconst data = generateDummyStats()\n\tconst json = JSON.stringify(data, null, '\\t')\n\n\tif (typeof window !== 'undefined') {\n\t\t// Browser environment - trigger download\n\t\tconst blob = new Blob([json], { type: 'application/json' })\n\t\tconst url = URL.createObjectURL(blob)\n\t\tconst a = document.createElement('a')\n\t\ta.href = url\n\t\ta.download = filepath\n\t\ta.click()\n\t\tURL.revokeObjectURL(url)\n\t} else {\n\t\t// Node environment\n\t\ttry {\n\t\t\tconst fs = require('fs')\n\t\t\tfs.writeFileSync(filepath, json)\n\t\t\tconsole.log(`✓ Dummy data saved to ${filepath}`)\n\t\t} catch (err) {\n\t\t\tconsole.error('Failed to save dummy data:', err)\n\t\t}\n\t}\n}\n","// packages/core/src/index.ts\n// Framework-agnostic core logic\n\n// Re-export everything from types\nexport type {\n\tColorScheme,\n\tContributionDay,\n\tContributionWeek,\n\tProfile,\n\tGitStatsData,\n\tExperienceEntry,\n\tDataSource,\n\tPlatform,\n\tProfileStats,\n\tStatsTotals,\n\tStatsMetadata,\n\tCustomStatCalculator,\n\tCustomStatCalculatorParams,\n} from './types/index.js'\n\n// Export API functions\nexport { fetchGitStats as fetchGitStatsAPI } from './api/fetchGitStats.js'\n\n// Export utility functions with explicit imports\nexport {\n\tgenerateDummyStats,\n\tgenerateDummyContributions,\n\tgenerateMultiProfileDummyStats,\n\tsaveDummyDataToFile,\n} from './utils/generateDummyData.js'\n\n// Core data fetching (framework-agnostic)\nimport type { GitStatsData } from './types/index.js'\n\nexport interface FetchOptions {\n\tdataUrl: string\n\tcacheTTL?: number\n\tcacheKey?: string\n\tuseStaleCache?: boolean\n}\n\nexport interface DataResult<T> {\n\tdata: T | null\n\terror: Error | null\n\tsource: 'static' | 'cache' | 'mock' | 'dummy' | null\n\tisDummy: boolean\n}\n\n/**\n * Framework-agnostic data fetcher\n */\nexport async function fetchGitStats(\n\toptions: FetchOptions\n): Promise<DataResult<GitStatsData>> {\n\tconst { dataUrl, cacheKey = 'git_stats_cache', useStaleCache = true } = options\n\n\ttry {\n\t\t// Try static file first\n\t\tconst response = await fetch(dataUrl)\n\t\tif (response.ok) {\n\t\t\tconst data = await response.json()\n\t\t\t// Cache the data\n\t\t\tif (typeof window !== 'undefined') {\n\t\t\t\tlocalStorage.setItem(\n\t\t\t\t\tcacheKey,\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t...data,\n\t\t\t\t\t\tcachedAt: Date.now(),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tdata,\n\t\t\t\terror: null,\n\t\t\t\tsource: 'static',\n\t\t\t\tisDummy: data.metadata?.isDummy === true,\n\t\t\t}\n\t\t}\n\t} catch (err) {\n\t\tconsole.warn('Failed to fetch from static file:', err)\n\t}\n\n\t// Try cache\n\tif (useStaleCache && typeof window !== 'undefined') {\n\t\ttry {\n\t\t\tconst cached = localStorage.getItem(cacheKey)\n\t\t\tif (cached) {\n\t\t\t\tconst data = JSON.parse(cached)\n\t\t\t\treturn {\n\t\t\t\t\tdata,\n\t\t\t\t\terror: null,\n\t\t\t\t\tsource: 'cache',\n\t\t\t\t\tisDummy: data.metadata?.isDummy === true,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.warn('Failed to load from cache:', err)\n\t\t}\n\t}\n\n\t// Fallback to mock\n\tconst mockData = generateMockData()\n\treturn {\n\t\tdata: mockData,\n\t\terror: null,\n\t\tsource: 'mock',\n\t\tisDummy: false,\n\t}\n}\n\n/**\n * Format last updated time\n */\nexport function formatLastUpdated(dateString: string): string {\n\tconst date = new Date(dateString)\n\tconst now = new Date()\n\tconst diffHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60))\n\n\tif (diffHours < 1) return 'just now'\n\tif (diffHours < 24) return `${diffHours} hours ago`\n\n\tconst diffDays = Math.floor(diffHours / 24)\n\tif (diffDays === 1) return 'yesterday'\n\tif (diffDays < 7) return `${diffDays} days ago`\n\n\treturn date.toLocaleDateString('en-US', {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,\n\t})\n}\n\n/**\n * Get contribution level (0-4)\n */\nexport function getContributionLevel(count: number): number {\n\tif (count === 0) return 0\n\tif (count <= 3) return 1\n\tif (count <= 6) return 2\n\tif (count <= 9) return 3\n\treturn 4\n}\n\n/**\n * Calculate years of experience\n */\nexport function calculateYearsExperience(\n\texperienceData: { startDate: string; endDate: string | null; skills?: string[] }[]\n): number {\n\tif (experienceData.length === 0) return 0\n\n\tconst skillExperience: Record<string, number> = {}\n\n\texperienceData.forEach((exp) => {\n\t\tconst end = exp.endDate ? new Date(exp.endDate) : new Date()\n\t\tconst start = new Date(exp.startDate)\n\t\tconst years = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24 * 365.25)\n\n\t\texp.skills?.forEach((skill) => {\n\t\t\tif (!skillExperience[skill]) {\n\t\t\t\tskillExperience[skill] = 0\n\t\t\t}\n\t\t\tskillExperience[skill] += years\n\t\t})\n\t})\n\n\treturn Math.max(...Object.values(skillExperience), 0)\n}\n\n// Mock data generator\nfunction generateMockData(): GitStatsData {\n\treturn {\n\t\tlastUpdated: new Date().toISOString(),\n\t\tprofiles: [\n\t\t\t{\n\t\t\t\tusername: 'mockuser',\n\t\t\t\tplatform: 'github',\n\t\t\t\tstats: {\n\t\t\t\t\tprojectCount: 30,\n\t\t\t\t\tcommitCount: 2500,\n\t\t\t\t\tcontributions: [],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\ttotals: {\n\t\t\tprojectCount: 30,\n\t\t\tcommitCount: 2500,\n\t\t},\n\t\tmetadata: {\n\t\t\tsource: 'mock',\n\t\t\tfetchedAt: Date.now(),\n\t\t},\n\t}\n}","import { ref, computed } from 'vue'\r\nimport {\r\n\tfetchGitStats,\r\n\tformatLastUpdated,\r\n\ttype GitStatsData,\r\n\ttype DataSource,\r\n} from '@git-stats-components/core'\r\n\r\nexport interface UseGitStatsConfig {\r\n\tdataUrl?: string\r\n\tcacheTTL?: number\r\n\tuseStaleCache?: boolean\r\n\tcacheKey?: string\r\n}\r\n\r\nexport function useGitStats(config: UseGitStatsConfig = {}) {\r\n\tconst {\r\n\t\tdataUrl = '/data/git-stats.json',\r\n\t\tcacheTTL = 24 * 60 * 60 * 1000,\r\n\t\tuseStaleCache = true,\r\n\t\tcacheKey = 'git_stats_cache',\r\n\t} = config\r\n\r\n\tconst loading = ref(false)\r\n\tconst error = ref<Error | null>(null)\r\n\tconst data = ref<GitStatsData | null>(null)\r\n\tconst dataSource = ref<DataSource | null>(null)\r\n\tconst isDummy = ref(false)\r\n\r\n\t/**\r\n\t * Load data with fallback strategy\r\n\t */\r\n\tasync function loadData(): Promise<GitStatsData | null> {\r\n\t\tloading.value = true\r\n\t\terror.value = null\r\n\r\n\t\ttry {\r\n\t\t\tconst result = await fetchGitStats({\r\n\t\t\t\tdataUrl,\r\n\t\t\t\tcacheTTL,\r\n\t\t\t\tcacheKey,\r\n\t\t\t\tuseStaleCache,\r\n\t\t\t})\r\n\r\n\t\t\tdata.value = result.data\r\n\t\t\terror.value = result.error\r\n\t\t\tdataSource.value = result.source\r\n\t\t\tisDummy.value = result.isDummy\r\n\r\n\t\t\treturn result.data\r\n\t\t} catch (err) {\r\n\t\t\terror.value =\r\n\t\t\t\terr instanceof Error ? err : new Error('Failed to load data')\r\n\t\t\treturn null\r\n\t\t} finally {\r\n\t\t\tloading.value = false\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Format \"last updated\" text\r\n\t */\r\n\tconst lastUpdatedText = computed(() => {\r\n\t\tif (!data.value?.lastUpdated) return ''\r\n\t\treturn formatLastUpdated(data.value.lastUpdated)\r\n\t})\r\n\r\n\t/**\r\n\t * Computed data source display text\r\n\t */\r\n\tconst dataSourceText = computed(() => {\r\n\t\tif (isDummy.value) {\r\n\t\t\treturn '⚠️ Using dummy data for testing'\r\n\t\t}\r\n\r\n\t\tswitch (dataSource.value) {\r\n\t\t\tcase 'static':\r\n\t\t\t\treturn 'Real-time data'\r\n\t\t\tcase 'cache':\r\n\t\t\t\treturn 'Cached data'\r\n\t\t\tcase 'mock':\r\n\t\t\t\treturn 'Sample data'\r\n\t\t\tdefault:\r\n\t\t\t\treturn ''\r\n\t\t}\r\n\t})\r\n\r\n\t// Auto-load on creation\r\n\tloadData()\r\n\r\n\treturn {\r\n\t\tdata,\r\n\t\tloading,\r\n\t\terror,\r\n\t\tdataSource,\r\n\t\tdataSourceText,\r\n\t\tlastUpdatedText,\r\n\t\tisDummy,\r\n\t\tloadData,\r\n\t}\r\n}\r\n","<template>\r\n\t<div class=\"git-contribution-graph\">\r\n\t\t<!-- Header -->\r\n\t\t<div class=\"graph-header\">\r\n\t\t\t<div class=\"header-info\">\r\n\t\t\t\t<h5 class=\"contribution-count\">\r\n\t\t\t\t\t{{ totalContributions.toLocaleString() }} contributions in\r\n\t\t\t\t\tthe last year\r\n\t\t\t\t</h5>\r\n\t\t\t\t<small\r\n\t\t\t\t\tclass=\"data-source-text\"\r\n\t\t\t\t\t:class=\"{ 'is-dummy': isDummy }\"\r\n\t\t\t\t>\r\n\t\t\t\t\t{{ dataSourceText }}\r\n\t\t\t\t</small>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"header-actions\" v-if=\"showSettings\">\r\n\t\t\t\t<button\r\n\t\t\t\t\tclass=\"settings-btn\"\r\n\t\t\t\t\ttype=\"button\"\r\n\t\t\t\t\t@click=\"toggleSettings\"\r\n\t\t\t\t>\r\n\t\t\t\t\t<slot name=\"settings-icon\">⚙️</slot>\r\n\t\t\t\t\tSettings\r\n\t\t\t\t</button>\r\n\t\t\t\t<div v-if=\"settingsOpen\" class=\"settings-dropdown\">\r\n\t\t\t\t\t<button\r\n\t\t\t\t\t\tv-for=\"scheme in colorSchemes\"\r\n\t\t\t\t\t\t:key=\"scheme\"\r\n\t\t\t\t\t\t@click=\"changeColorScheme(scheme)\"\r\n\t\t\t\t\t\tclass=\"settings-item\"\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t{{ scheme }} theme\r\n\t\t\t\t\t</button>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\r\n\t\t<!-- Loading state -->\r\n\t\t<div v-if=\"loading\" class=\"loading-state\">\r\n\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t<span>Loading contributions...</span>\r\n\t\t</div>\r\n\r\n\t\t<!-- Contribution grid -->\r\n\t\t<div v-else class=\"graph-container\">\r\n\t\t\t<div class=\"graph-content-wrapper\">\r\n\t\t\t\t<!-- Month labels -->\r\n\t\t\t\t<div class=\"months-row\">\r\n\t\t\t\t\t<div class=\"month-spacer\"></div>\r\n\t\t\t\t\t<div class=\"months-container\">\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tv-for=\"month in monthLabels\"\r\n\t\t\t\t\t\t\t:key=\"`${month.year}-${month.month}`\"\r\n\t\t\t\t\t\t\tclass=\"month-label\"\r\n\t\t\t\t\t\t\t:style=\"{\r\n\t\t\t\t\t\t\t\tgridColumn: `${month.week + 1} / span 1`,\r\n\t\t\t\t\t\t\t}\"\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t{{ month.label }}\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\r\n\t\t\t\t<!-- Grid with day labels -->\r\n\t\t\t\t<div class=\"grid-container\">\r\n\t\t\t\t\t<!-- Day labels -->\r\n\t\t\t\t\t<div class=\"day-labels\">\r\n\t\t\t\t\t\t<div class=\"day-label\">Mon</div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t\t<div class=\"day-label\">Wed</div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t\t<div class=\"day-label\">Fri</div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t</div>\r\n\r\n\t\t\t\t\t<!-- Contribution squares -->\r\n\t\t\t\t\t<div class=\"contribution-grid\">\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tv-for=\"week in contributionData\"\r\n\t\t\t\t\t\t\t:key=\"week.weekStart\"\r\n\t\t\t\t\t\t\tclass=\"contribution-week\"\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\t\tv-for=\"day in week.days\"\r\n\t\t\t\t\t\t\t\t:key=\"day.date\"\r\n\t\t\t\t\t\t\t\tclass=\"contribution-day\"\r\n\t\t\t\t\t\t\t\t:class=\"getContributionLevel(day.count)\"\r\n\t\t\t\t\t\t\t\t:title=\"getTooltipText(day)\"\r\n\t\t\t\t\t\t\t\t@click=\"onDayClick(day)\"\r\n\t\t\t\t\t\t\t></div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Legend -->\r\n\t\t\t<div class=\"graph-footer\">\r\n\t\t\t\t<small class=\"last-updated\" v-if=\"lastUpdatedText\">\r\n\t\t\t\t\tLast updated: {{ lastUpdatedText }}\r\n\t\t\t\t</small>\r\n\t\t\t\t<div class=\"legend\">\r\n\t\t\t\t\t<small class=\"legend-label\">Less</small>\r\n\t\t\t\t\t<div class=\"legend-squares\">\r\n\t\t\t\t\t\t<div class=\"contribution-day level-0\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-1\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-2\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-3\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-4\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<small class=\"legend-label\">More</small>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport { useGitStats } from '../composables/useGitStats'\r\nimport {\r\n\ttype ColorScheme,\r\n\ttype ContributionWeek,\r\n} from '@git-stats-components/core'\r\n\r\ninterface ProcessedWeek {\r\n\tweekStart: string\r\n\tdays: ProcessedDay[]\r\n}\r\n\r\ninterface ProcessedDay {\r\n\tdate: string\r\n\tcount: number\r\n\tweekday: number\r\n}\r\n\r\ninterface MonthLabel {\r\n\tweek: number\r\n\tmonth: number\r\n\tyear: number\r\n\tlabel: string\r\n}\r\n\r\ninterface Props {\r\n\tdataUrl?: string\r\n\tprofileIndex?: number\r\n\tcolorScheme?: ColorScheme\r\n\tshowSettings?: boolean\r\n\tcacheTTL?: number\r\n}\r\n\r\ninterface Emits {\r\n\t(e: 'day-click', data: { date: string; count: number }): void\r\n\t(e: 'color-scheme-change', scheme: ColorScheme): void\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n\tdataUrl: '/data/git-stats.json',\r\n\tprofileIndex: 0,\r\n\tcolorScheme: 'green',\r\n\tshowSettings: true,\r\n\tcacheTTL: 24 * 60 * 60 * 1000,\r\n})\r\n\r\nconst emit = defineEmits<Emits>()\r\n\r\n// Use the shared composable\r\nconst { data, loading, dataSourceText, lastUpdatedText, isDummy } = useGitStats(\r\n\t{\r\n\t\tdataUrl: props.dataUrl,\r\n\t\tcacheTTL: props.cacheTTL,\r\n\t}\r\n)\r\n\r\nconst currentColorScheme = ref<ColorScheme>(props.colorScheme)\r\nconst settingsOpen = ref(false)\r\nconst colorSchemes: ColorScheme[] = ['green', 'blue', 'purple', 'orange']\r\nconst contributionData = ref<ProcessedWeek[]>([])\r\nconst monthLabels = ref<MonthLabel[]>([])\r\n\r\n// Process contribution data when loaded\r\nwatch(\r\n\tdata,\r\n\t(newData) => {\r\n\t\tif (newData?.profiles?.[props.profileIndex]?.stats?.contributions) {\r\n\t\t\tconst contributions =\r\n\t\t\t\tnewData.profiles[props.profileIndex].stats.contributions\r\n\t\t\tif (contributions) {\r\n\t\t\t\tcontributionData.value = processContributions(contributions)\r\n\t\t\t\tgenerateMonthLabels()\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tcontributionData.value = []\r\n\t\t}\r\n\t},\r\n\t{ immediate: true }\r\n)\r\n\r\nconst totalContributions = computed(() => {\r\n\tif (!contributionData.value || contributionData.value.length === 0) {\r\n\t\treturn 0\r\n\t}\r\n\r\n\treturn contributionData.value.reduce((total, week) => {\r\n\t\tif (!week.days || !Array.isArray(week.days)) {\r\n\t\t\treturn total\r\n\t\t}\r\n\t\treturn (\r\n\t\t\ttotal +\r\n\t\t\tweek.days.reduce((weekTotal, day) => {\r\n\t\t\t\treturn weekTotal + (day.count || 0)\r\n\t\t\t}, 0)\r\n\t\t)\r\n\t}, 0)\r\n})\r\n\r\nfunction processContributions(\r\n\tcontributions: ContributionWeek[]\r\n): ProcessedWeek[] {\r\n\tif (!contributions || !Array.isArray(contributions)) {\r\n\t\treturn generateEmptyWeeks()\r\n\t}\r\n\r\n\tconst weeks = contributions.map((week) => ({\r\n\t\tweekStart: week.firstDay || '',\r\n\t\tdays: week.contributionDays.map((day) => ({\r\n\t\t\tdate: day.date || '',\r\n\t\t\tcount: day.contributionCount ?? 0,\r\n\t\t\tweekday: day.weekday || 0,\r\n\t\t})),\r\n\t}))\r\n\r\n\twhile (weeks.length < 53) {\r\n\t\tweeks.push(generateEmptyWeek())\r\n\t}\r\n\r\n\treturn weeks\r\n}\r\n\r\nfunction generateEmptyWeeks(): ProcessedWeek[] {\r\n\tconst weeks: ProcessedWeek[] = []\r\n\tfor (let i = 0; i < 53; i++) {\r\n\t\tweeks.push(generateEmptyWeek())\r\n\t}\r\n\treturn weeks\r\n}\r\n\r\nfunction generateEmptyWeek(): ProcessedWeek {\r\n\tconst days: ProcessedDay[] = []\r\n\tfor (let i = 0; i < 7; i++) {\r\n\t\tdays.push({ date: '', count: 0, weekday: i })\r\n\t}\r\n\treturn { weekStart: '', days }\r\n}\r\n\r\nfunction generateMonthLabels(): void {\r\n\tif (!contributionData.value || contributionData.value.length === 0) {\r\n\t\tmonthLabels.value = []\r\n\t\treturn\r\n\t}\r\n\r\n\tconst monthPositions: MonthLabel[] = []\r\n\tlet lastMonth = -1\r\n\tlet lastYear = -1\r\n\r\n\tcontributionData.value.forEach((week, weekIndex) => {\r\n\t\tif (!week.days || week.days.length === 0) return\r\n\r\n\t\tconst firstDay = week.days[0].date\r\n\t\tif (!firstDay) return\r\n\r\n\t\tconst dateParts = firstDay.split('-')\r\n\t\tif (dateParts.length !== 3) return\r\n\r\n\t\tconst [year, month] = dateParts.map(Number)\r\n\t\tif (isNaN(year) || isNaN(month)) return\r\n\r\n\t\tif (month !== lastMonth || year !== lastYear) {\r\n\t\t\tconst date = new Date(year, month - 1, 1)\r\n\t\t\tmonthPositions.push({\r\n\t\t\t\tweek: weekIndex,\r\n\t\t\t\tmonth: month - 1,\r\n\t\t\t\tyear: year,\r\n\t\t\t\tlabel: date.toLocaleDateString('en-US', { month: 'short' }),\r\n\t\t\t})\r\n\t\t\tlastMonth = month\r\n\t\t\tlastYear = year\r\n\t\t}\r\n\t})\r\n\r\n\tmonthLabels.value = monthPositions\r\n}\r\n\r\nfunction getContributionLevel(count: number): string {\r\n\tconst level = getContributionLevelNumber(count)\r\n\treturn `level-${level} ${currentColorScheme.value}`\r\n}\r\n\r\nfunction getContributionLevelNumber(count: number): number {\r\n\tif (count === 0) return 0\r\n\tif (count <= 3) return 1\r\n\tif (count <= 6) return 2\r\n\tif (count <= 9) return 3\r\n\treturn 4\r\n}\r\n\r\nfunction getTooltipText(day: ProcessedDay): string {\r\n\tif (!day.date) return ''\r\n\r\n\tconst [year, month, dayNum] = day.date.split('-').map(Number)\r\n\tconst date = new Date(year, month - 1, dayNum)\r\n\r\n\tconst formattedDate = date.toLocaleDateString('en-US', {\r\n\t\tweekday: 'short',\r\n\t\tyear: 'numeric',\r\n\t\tmonth: 'short',\r\n\t\tday: 'numeric',\r\n\t})\r\n\r\n\tconst contributionText = day.count === 1 ? 'contribution' : 'contributions'\r\n\treturn `${day.count} ${contributionText} on ${formattedDate}`\r\n}\r\n\r\nfunction onDayClick(day: ProcessedDay): void {\r\n\temit('day-click', { date: day.date, count: day.count })\r\n}\r\n\r\nfunction toggleSettings(): void {\r\n\tsettingsOpen.value = !settingsOpen.value\r\n}\r\n\r\nfunction changeColorScheme(scheme: ColorScheme): void {\r\n\tcurrentColorScheme.value = scheme\r\n\tsettingsOpen.value = false\r\n\temit('color-scheme-change', scheme)\r\n}\r\n</script>\r\n\r\n<style scoped>\r\n.graph-content-wrapper {\r\n\tjustify-items: anchor-center;\r\n}\r\n.git-contribution-graph {\r\n\tfont-family:\r\n\t\t-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,\r\n\t\tArial, sans-serif;\r\n\tfont-size: 12px;\r\n\tbackground: transparent;\r\n\tcolor: #e6edf3;\r\n\tpadding: 16px;\r\n\tmax-width: 1200px;\r\n\tmargin: 0 auto;\r\n\twidth: 100%;\r\n}\r\n\r\n.graph-header {\r\n\tdisplay: flex;\r\n\tjustify-content: space-between;\r\n\talign-items: center;\r\n\tmargin-bottom: 16px;\r\n}\r\n\r\n.contribution-count {\r\n\tmargin: 0 0 4px 0;\r\n\tfont-size: 16px;\r\n\tfont-weight: 600;\r\n}\r\n\r\n.data-source-text {\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.data-source-text.is-dummy {\r\n\tcolor: #f85149;\r\n\tfont-weight: 600;\r\n\tbackground: rgba(248, 81, 73, 0.1);\r\n\tpadding: 2px 8px;\r\n\tborder-radius: 4px;\r\n}\r\n\r\n.settings-btn {\r\n\tbackground: transparent;\r\n\tborder: 1px solid #30363d;\r\n\tcolor: #7d8590;\r\n\tpadding: 6px 12px;\r\n\tborder-radius: 6px;\r\n\tcursor: pointer;\r\n\tfont-size: 12px;\r\n}\r\n\r\n.settings-btn:hover {\r\n\tbackground: #21262d;\r\n\tcolor: #e6edf3;\r\n}\r\n\r\n.header-actions {\r\n\tposition: relative;\r\n}\r\n\r\n.settings-dropdown {\r\n\tposition: absolute;\r\n\tright: 0;\r\n\ttop: 100%;\r\n\tmargin-top: 4px;\r\n\tbackground: #21262d;\r\n\tborder: 1px solid #30363d;\r\n\tborder-radius: 6px;\r\n\tpadding: 4px;\r\n\tz-index: 10;\r\n\tmin-width: 150px;\r\n}\r\n\r\n.settings-item {\r\n\tdisplay: block;\r\n\twidth: 100%;\r\n\tbackground: transparent;\r\n\tborder: none;\r\n\tcolor: #e6edf3;\r\n\tpadding: 8px 12px;\r\n\ttext-align: left;\r\n\tcursor: pointer;\r\n\tborder-radius: 4px;\r\n}\r\n\r\n.settings-item:hover {\r\n\tbackground: #30363d;\r\n}\r\n\r\n.loading-state {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tjustify-content: center;\r\n\tgap: 12px;\r\n\tpadding: 40px;\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.spinner {\r\n\twidth: 20px;\r\n\theight: 20px;\r\n\tborder: 2px solid #30363d;\r\n\tborder-top-color: #58a6ff;\r\n\tborder-radius: 50%;\r\n\tanimation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n\tto {\r\n\t\ttransform: rotate(360deg);\r\n\t}\r\n}\r\n\r\n.months-row {\r\n\tdisplay: flex;\r\n\tmargin-bottom: 4px;\r\n}\r\n\r\n.month-spacer {\r\n\twidth: 27px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.months-container {\r\n\tdisplay: grid;\r\n\tgrid-template-columns: repeat(53, 11px);\r\n\tgap: 2px;\r\n\tflex: 1;\r\n\tmargin-left: 3px;\r\n\tmin-width: 0;\r\n}\r\n\r\n.month-label {\r\n\tfont-size: 11px;\r\n\tcolor: #7d8590;\r\n\ttext-align: left;\r\n}\r\n\r\n.grid-container {\r\n\tdisplay: flex;\r\n\tgap: 3px;\r\n\tmin-width: fit-content;\r\n}\r\n\r\n.day-labels {\r\n\tdisplay: flex;\r\n\tflex-direction: column;\r\n\twidth: 24px;\r\n\tgap: 2px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.day-label {\r\n\theight: 11px;\r\n\tfont-size: 9px;\r\n\tcolor: #7d8590;\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n}\r\n\r\n.contribution-grid {\r\n\tdisplay: flex;\r\n\tgap: 2px;\r\n\tflex: 1;\r\n\tmin-width: 0;\r\n}\r\n\r\n.contribution-week {\r\n\tdisplay: flex;\r\n\tflex-direction: column;\r\n\tgap: 2px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.contribution-day {\r\n\twidth: 11px;\r\n\theight: 11px;\r\n\tborder-radius: 2px;\r\n\tcursor: pointer;\r\n\toutline: 1px solid rgba(27, 31, 36, 0.06);\r\n\toutline-offset: -1px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n/* Color schemes */\r\n.contribution-day.level-0.green {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.green {\r\n\tbackground-color: #0e4429;\r\n}\r\n.contribution-day.level-2.green {\r\n\tbackground-color: #006d32;\r\n}\r\n.contribution-day.level-3.green {\r\n\tbackground-color: #26a641;\r\n}\r\n.contribution-day.level-4.green {\r\n\tbackground-color: #39d353;\r\n}\r\n\r\n.contribution-day.level-0.blue {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.blue {\r\n\tbackground-color: #0a3069;\r\n}\r\n.contribution-day.level-2.blue {\r\n\tbackground-color: #1f6feb;\r\n}\r\n.contribution-day.level-3.blue {\r\n\tbackground-color: #58a6ff;\r\n}\r\n.contribution-day.level-4.blue {\r\n\tbackground-color: #79c0ff;\r\n}\r\n\r\n.contribution-day.level-0.purple {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.purple {\r\n\tbackground-color: #3b1e6d;\r\n}\r\n.contribution-day.level-2.purple {\r\n\tbackground-color: #8250df;\r\n}\r\n.contribution-day.level-3.purple {\r\n\tbackground-color: #a475f9;\r\n}\r\n.contribution-day.level-4.purple {\r\n\tbackground-color: #d2a8ff;\r\n}\r\n\r\n.contribution-day.level-0.orange {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.orange {\r\n\tbackground-color: #7d2d00;\r\n}\r\n.contribution-day.level-2.orange {\r\n\tbackground-color: #da7b00;\r\n}\r\n.contribution-day.level-3.orange {\r\n\tbackground-color: #ffa348;\r\n}\r\n.contribution-day.level-4.orange {\r\n\tbackground-color: #ffb366;\r\n}\r\n\r\n.contribution-day:hover {\r\n\toutline: 1px solid #c9d1d9;\r\n\toutline-offset: -1px;\r\n}\r\n\r\n.graph-footer {\r\n\tdisplay: flex;\r\n\tjustify-content: space-between;\r\n\talign-items: center;\r\n\tmargin-top: 8px;\r\n}\r\n\r\n.last-updated {\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.legend {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tgap: 4px;\r\n}\r\n\r\n.legend-label {\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.legend-squares {\r\n\tdisplay: flex;\r\n\tgap: 2px;\r\n}\r\n\r\n.legend-squares .contribution-day {\r\n\tcursor: default;\r\n}\r\n\r\n.legend-squares .contribution-day:hover {\r\n\toutline: none;\r\n}\r\n\r\n/* Mobile responsive */\r\n@media (max-width: 768px) {\r\n\t.git-contribution-graph {\r\n\t\tpadding: 12px;\r\n\t\tfont-size: 11px;\r\n\t\toverflow-x: auto;\r\n\t}\r\n\t.months-container {\r\n\t\tgrid-template-columns: repeat(53, 10px);\r\n\t\tgap: 1px;\r\n\t}\r\n\t.grid-container {\r\n\t\tgap: 2px;\r\n\t}\r\n\t.day-labels {\r\n\t\twidth: 20px;\r\n\t}\r\n\t.day-label {\r\n\t\theight: 10px;\r\n\t\tfont-size: 8px;\r\n\t}\r\n\t.contribution-grid {\r\n\t\tgap: 1px;\r\n\t}\r\n\t.contribution-week {\r\n\t\tgap: 1px;\r\n\t}\r\n\t.contribution-day {\r\n\t\twidth: 10px;\r\n\t\theight: 10px;\r\n\t}\r\n\t.settings-btn {\r\n\t\tfont-size: 10px;\r\n\t\tpadding: 4px 8px;\r\n\t}\r\n\t.contribution-count {\r\n\t\tfont-size: 14px;\r\n\t}\r\n}\r\n\r\n@media (max-width: 480px) {\r\n\t.graph-header {\r\n\t\tflex-direction: column;\r\n\t\talign-items: flex-start;\r\n\t\tgap: 8px;\r\n\t}\r\n}\r\n</style>\r\n","<template>\r\n\t<div class=\"git-stats-breakdown\">\r\n\t\t<div class=\"stats-grid\">\r\n\t\t\t<!-- Years Experience -->\r\n\t\t\t<div class=\"stat-card\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-experience\">⏱️</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div class=\"stat-value\">{{ yearsExperience }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">Years Experience</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Projects -->\r\n\t\t\t<div class=\"stat-card\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-projects\">📦</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div v-if=\"loading\" class=\"stat-loading\">\r\n\t\t\t\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div v-else class=\"stat-value\">{{ totalProjects }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">Projects</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Commits -->\r\n\t\t\t<div class=\"stat-card\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-commits\">💻</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div v-if=\"loading\" class=\"stat-loading\">\r\n\t\t\t\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div v-else class=\"stat-value\">{{ totalCommits }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">Commits</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Custom Stat -->\r\n\t\t\t<div class=\"stat-card\" v-if=\"showCustomStat\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-custom\">☕</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div v-if=\"loading\" class=\"stat-loading\">\r\n\t\t\t\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div v-else class=\"stat-value\">{{ customStatValue }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">\r\n\t\t\t\t\t\t<slot name=\"custom-stat-label\">Coffee Consumed</slot>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\r\n\t\t<!-- Footer -->\r\n\t\t<div class=\"stats-footer\">\r\n\t\t\t<small v-if=\"dataSourceText\" class=\"data-source\">\r\n\t\t\t\t{{ dataSourceText }}\r\n\t\t\t\t<span v-if=\"lastUpdatedText\"> · {{ lastUpdatedText }}</span>\r\n\t\t\t</small>\r\n\t\t</div>\r\n\t</div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport { useGitStats } from '../composables/useGitStats'\r\nimport {\r\n\tcalculateYearsExperience,\r\n\ttype ExperienceEntry,\r\n\ttype CustomStatCalculator,\r\n\ttype CustomStatCalculatorParams,\r\n} from '@git-stats-components/core'\r\n\r\ninterface Props {\r\n\tdataUrl?: string\r\n\tprofileIndexes?: number[]\r\n\texperienceData?: ExperienceEntry[]\r\n\tshowCustomStat?: boolean\r\n\tcustomStatCalculator?: CustomStatCalculator | null\r\n\tcacheTTL?: number\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n\tdataUrl: '/data/git-stats.json',\r\n\tprofileIndexes: () => [],\r\n\texperienceData: () => [],\r\n\tshowCustomStat: true,\r\n\tcustomStatCalculator: null,\r\n\tcacheTTL: 24 * 60 * 60 * 1000,\r\n})\r\n\r\n// Use the shared composable\r\nconst { data, loading, dataSourceText, lastUpdatedText } = useGitStats({\r\n\tdataUrl: props.dataUrl,\r\n\tcacheTTL: props.cacheTTL,\r\n})\r\n\r\nconst totalProjects = ref(0)\r\nconst totalCommits = ref(0)\r\n\r\n// Calculate totals when data loads\r\nwatch(\r\n\tdata,\r\n\t(newData) => {\r\n\t\tif (!newData) return\r\n\r\n\t\t// If profileIndexes specified, sum only those profiles\r\n\t\tif (props.profileIndexes.length > 0) {\r\n\t\t\tlet projects = 0\r\n\t\t\tlet commits = 0\r\n\r\n\t\t\tprops.profileIndexes.forEach((index) => {\r\n\t\t\t\tconst profile = newData.profiles?.[index]\r\n\t\t\t\tif (profile?.stats) {\r\n\t\t\t\t\tprojects += profile.stats.projectCount || 0\r\n\t\t\t\t\tcommits += profile.stats.commitCount || 0\r\n\t\t\t\t}\r\n\t\t\t})\r\n\r\n\t\t\ttotalProjects.value = projects\r\n\t\t\ttotalCommits.value = commits\r\n\t\t} else {\r\n\t\t\t// Use totals from data (aggregates all profiles)\r\n\t\t\ttotalProjects.value = newData.totals?.projectCount || 0\r\n\t\t\ttotalCommits.value = newData.totals?.commitCount || 0\r\n\t\t}\r\n\t},\r\n\t{ immediate: true }\r\n)\r\n\r\n// Calculate years of experience using core utility\r\nconst yearsExperience = computed(() => {\r\n\tconst years = calculateYearsExperience(props.experienceData)\r\n\treturn years.toFixed(1)\r\n})\r\n\r\n// Custom stat calculation\r\nconst customStatValue = computed(() => {\r\n\tif (props.customStatCalculator) {\r\n\t\tconst params: CustomStatCalculatorParams = {\r\n\t\t\tprojects: totalProjects.value,\r\n\t\t\tcommits: totalCommits.value,\r\n\t\t\tyears: parseFloat(yearsExperience.value),\r\n\t\t}\r\n\t\treturn props.customStatCalculator(params)\r\n\t}\r\n\r\n\t// Default: fun coffee calculation\r\n\tconst kA = 1.5\r\n\tconst kB = 1.2\r\n\tconst kC = 1.5\r\n\r\n\tconst cups =\r\n\t\ttotalProjects.value * kA +\r\n\t\ttotalCommits.value * kB +\r\n\t\tparseFloat(yearsExperience.value) * kC\r\n\r\n\treturn cups.toFixed(2)\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.git-stats-breakdown {\r\n\tfont-family:\r\n\t\t-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,\r\n\t\tArial, sans-serif;\r\n\tpadding: 40px 20px;\r\n\tmax-width: 1200px;\r\n\tmargin: 0 auto;\r\n}\r\n\r\n.stats-grid {\r\n\tdisplay: grid;\r\n\tgrid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\r\n\tgap: 24px;\r\n\tmargin-bottom: 24px;\r\n}\r\n\r\n.stat-card {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tgap: 16px;\r\n\tpadding: 24px;\r\n\tbackground: rgba(255, 255, 255, 0.05);\r\n\tborder-radius: 12px;\r\n\tborder: 1px solid rgba(255, 255, 255, 0.1);\r\n\ttransition: all 0.3s ease;\r\n}\r\n\r\n.stat-card:hover {\r\n\tbackground: rgba(255, 255, 255, 0.08);\r\n\tborder-color: rgba(255, 255, 255, 0.2);\r\n\ttransform: translateY(-2px);\r\n}\r\n\r\n.stat-icon {\r\n\tfont-size: 48px;\r\n\tline-height: 1;\r\n\topacity: 0.9;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.stat-content {\r\n\tflex: 1;\r\n\tmin-width: 0;\r\n}\r\n\r\n.stat-value {\r\n\tfont-size: 32px;\r\n\tfont-weight: bold;\r\n\tline-height: 1.2;\r\n\tcolor: #e6edf3;\r\n\tmargin-bottom: 4px;\r\n}\r\n\r\n.stat-label {\r\n\tfont-size: 14px;\r\n\tcolor: #7d8590;\r\n\ttext-transform: uppercase;\r\n\tletter-spacing: 0.5px;\r\n}\r\n\r\n.stat-loading {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tjustify-content: center;\r\n\theight: 38px;\r\n}\r\n\r\n.spinner {\r\n\twidth: 24px;\r\n\theight: 24px;\r\n\tborder: 3px solid rgba(255, 255, 255, 0.1);\r\n\tborder-top-color: #58a6ff;\r\n\tborder-radius: 50%;\r\n\tanimation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n\tto {\r\n\t\ttransform: rotate(360deg);\r\n\t}\r\n}\r\n\r\n.stats-footer {\r\n\tdisplay: flex;\r\n\tflex-direction: column;\r\n\talign-items: center;\r\n\tgap: 8px;\r\n\tpadding-top: 16px;\r\n\tborder-top: 1px solid rgba(255, 255, 255, 0.1);\r\n}\r\n\r\n.data-source {\r\n\tfont-size: 12px;\r\n\tcolor: #7d8590;\r\n\ttext-align: center;\r\n}\r\n\r\n/* Responsive */\r\n@media (max-width: 768px) {\r\n\t.git-stats-breakdown {\r\n\t\tpadding: 20px 12px;\r\n\t}\r\n\t.stats-grid {\r\n\t\tgrid-template-columns: 1fr;\r\n\t\tgap: 16px;\r\n\t}\r\n\t.stat-card {\r\n\t\tpadding: 16px;\r\n\t}\r\n\t.stat-icon {\r\n\t\tfont-size: 36px;\r\n\t}\r\n\t.stat-value {\r\n\t\tfont-size: 24px;\r\n\t}\r\n\t.stat-label {\r\n\t\tfont-size: 12px;\r\n\t}\r\n}\r\n\r\n@media (max-width: 480px) {\r\n\t.stat-card {\r\n\t\tflex-direction: column;\r\n\t\ttext-align: center;\r\n\t}\r\n\t.stat-content {\r\n\t\twidth: 100%;\r\n\t}\r\n}\r\n</style>\r\n","// Main entry point for git-stats-components\n\nimport type { App } from 'vue'\nimport ContributionGraph from './components/ContributionGraph.vue'\nimport StatsBreakdown from './components/StatsBreakdown.vue'\nimport { useGitStats } from './composables/useGitStats'\n\n// Re-export everything from core\nexport * from '@git-stats-components/core'\n\n// Export Vue-specific components and composables\nexport { ContributionGraph, StatsBreakdown, useGitStats }\n\n// Auto-import styles\nimport './styles/index.css'\n\n// Plugin for Vue.use()\nexport interface VueGitStatsPlugin {\n\tinstall: (app: App) => void\n}\n\nconst VueGitStats: VueGitStatsPlugin = {\n\tinstall(app: App) {\n\t\tapp.component('ContributionGraph', ContributionGraph)\n\t\tapp.component('StatsBreakdown', StatsBreakdown)\n\t},\n}\n\n// Export as default for Vue.use()\nexport default VueGitStats\n"],"names":["fetchGitStats","url","__async","response","generateDummyContributions","weeks","now","endDate","startDate","currentDate","week","weekData","day","isInFuture","isWeekend","dayCount","generateDummyStats","options","username","platform","projectCount","commitCount","contributions","total","sum","generateMultiProfileDummyStats","githubProfile","gitlabProfile","saveDummyDataToFile","filepath","data","json","blob","a","err","dataUrl","cacheKey","useStaleCache","__spreadProps","__spreadValues","_a","cached","_b","generateMockData","formatLastUpdated","dateString","date","diffHours","diffDays","getContributionLevel","count","calculateYearsExperience","experienceData","skillExperience","exp","end","start","years","skill","useGitStats","config","cacheTTL","loading","ref","error","dataSource","isDummy","loadData","result","lastUpdatedText","computed","dataSourceText","props","__props","emit","__emit","currentColorScheme","settingsOpen","colorSchemes","contributionData","monthLabels","watch","newData","_c","processContributions","generateMonthLabels","totalContributions","weekTotal","generateEmptyWeeks","generateEmptyWeek","i","days","monthPositions","lastMonth","lastYear","weekIndex","firstDay","dateParts","year","month","getContributionLevelNumber","getTooltipText","dayNum","formattedDate","contributionText","onDayClick","toggleSettings","changeColorScheme","scheme","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_normalizeClass","_unref","_hoisted_5","_renderSlot","_ctx","_hoisted_6","_Fragment","_renderList","$event","_hoisted_7","_hoisted_8","_cache","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_normalizeStyle","_hoisted_13","_hoisted_14","_hoisted_16","_hoisted_17","totalProjects","totalCommits","projects","commits","index","profile","yearsExperience","customStatValue","params","_hoisted_15","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_createTextVNode","_hoisted_25","VueGitStats","app","ContributionGraph","StatsBreakdown"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(l,t){typeof exports=="object"&&typeof module!="undefined"?t(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],t):(l=typeof globalThis!="undefined"?globalThis:l||self,t(l.VueGitStats={},l.Vue))})(this,function(l,t){"use strict";var zt=Object.defineProperty,Jt=Object.defineProperties;var Kt=Object.getOwnPropertyDescriptors;var Y=Object.getOwnPropertySymbols;var Ht=Object.prototype.hasOwnProperty,Qt=Object.prototype.propertyIsEnumerable;var R=(l,t,k)=>t in l?zt(l,t,{enumerable:!0,configurable:!0,writable:!0,value:k}):l[t]=k,W=(l,t)=>{for(var k in t||(t={}))Ht.call(t,k)&&R(l,k,t[k]);if(Y)for(var k of Y(t))Qt.call(t,k)&&R(l,k,t[k]);return l},q=(l,t)=>Jt(l,Kt(t));var F=(l,t,k)=>new Promise((L,_)=>{var x=D=>{try{N(k.next(D))}catch(V){_(V)}},I=D=>{try{N(k.throw(D))}catch(V){_(V)}},N=D=>D.done?L(D.value):Promise.resolve(D.value).then(x,I);N((k=k.apply(l,t)).next())});function k(o){return F(this,null,function*(){const a=yield fetch(o);if(!a.ok)throw new Error(`Failed to fetch git stats: ${a.statusText}`);return a.json()})}function L(){const o=[],a=new Date,r=new Date(a);r.setDate(r.getDate()-r.getDay());const c=new Date(r);c.setDate(c.getDate()-52*7);const d=new Date(c);for(let p=0;p<53;p++){const m={weekStart:new Date(d).toISOString().split("T")[0],contributionDays:[]};for(let s=0;s<7;s++){const y=d>a,b=s===0||s===6;let u=0;y||(b?u=Math.random()<.3?Math.floor(Math.random()*5):0:u=Math.floor(Math.random()*15)),m.contributionDays.push({date:new Date(d).toISOString().split("T")[0],contributionCount:u,weekday:s}),d.setDate(d.getDate()+1)}o.push(m)}return o}function _(o={}){const{username:a="demo-user",platform:r="github",projectCount:c=30,commitCount:d=2500}=o,p=L();return p.reduce((m,s)=>m+s.contributionDays.reduce((y,b)=>y+b.contributionCount,0),0),{lastUpdated:new Date().toISOString(),profiles:[{username:a,platform:r,stats:{projectCount:c,commitCount:d,contributions:p.map(m=>({firstDay:m.weekStart,contributionDays:m.contributionDays}))}}],totals:{projectCount:c,commitCount:d},metadata:{fetchedAt:Date.now(),source:"dummy_data",isDummy:!0}}}function x(){const o=_({username:"demo-github",platform:"github",projectCount:45,commitCount:2847}),a=_({username:"demo-gitlab",platform:"gitlab",projectCount:7,commitCount:523});return{lastUpdated:new Date().toISOString(),profiles:[o.profiles[0],a.profiles[0]],totals:{projectCount:52,commitCount:3370},metadata:{fetchedAt:Date.now(),source:"dummy_data",isDummy:!0}}}function I(o="dummy-git-stats.json"){const a=_(),r=JSON.stringify(a,null," ");if(typeof window!="undefined"){const c=new Blob([r],{type:"application/json"}),d=URL.createObjectURL(c),p=document.createElement("a");p.href=d,p.download=o,p.click(),URL.revokeObjectURL(d)}else try{require("fs").writeFileSync(o,r),console.log(`✓ Dummy data saved to ${o}`)}catch(c){console.error("Failed to save dummy data:",c)}}function N(o){return F(this,null,function*(){var p,m;const{dataUrl:a,cacheKey:r="git_stats_cache",useStaleCache:c=!0}=o;try{const s=yield fetch(a);if(s.ok){const y=yield s.json();return typeof window!="undefined"&&localStorage.setItem(r,JSON.stringify(q(W({},y),{cachedAt:Date.now()}))),{data:y,error:null,source:"static",isDummy:((p=y.metadata)==null?void 0:p.isDummy)===!0}}}catch(s){console.warn("Failed to fetch from static file:",s)}if(c&&typeof window!="undefined")try{const s=localStorage.getItem(r);if(s){const y=JSON.parse(s);return{data:y,error:null,source:"cache",isDummy:((m=y.metadata)==null?void 0:m.isDummy)===!0}}}catch(s){console.warn("Failed to load from cache:",s)}return{data:z(),error:null,source:"mock",isDummy:!1}})}function D(o){const a=new Date(o),r=new Date,c=Math.floor((r.getTime()-a.getTime())/(1e3*60*60));if(c<1)return"just now";if(c<24)return`${c} hours ago`;const d=Math.floor(c/24);return d===1?"yesterday":d<7?`${d} days ago`:a.toLocaleDateString("en-US",{month:"short",day:"numeric",year:a.getFullYear()!==r.getFullYear()?"numeric":void 0})}function V(o){return o===0?0:o<=3?1:o<=6?2:o<=9?3:4}function O(o){if(o.length===0)return 0;const a={};return o.forEach(r=>{var m;const c=r.endDate?new Date(r.endDate):new Date,d=new Date(r.startDate),p=(c.getTime()-d.getTime())/(1e3*60*60*24*365.25);(m=r.skills)==null||m.forEach(s=>{a[s]||(a[s]=0),a[s]+=p})}),Math.max(...Object.values(a),0)}function z(){return{lastUpdated:new Date().toISOString(),profiles:[{username:"mockuser",platform:"github",stats:{projectCount:30,commitCount:2500,contributions:[]}}],totals:{projectCount:30,commitCount:2500},metadata:{source:"mock",fetchedAt:Date.now()}}}function M(o={}){const{dataUrl:a="/data/git-stats.json",cacheTTL:r=24*60*60*1e3,useStaleCache:c=!0,cacheKey:d="git_stats_cache"}=o,p=t.ref(!1),m=t.ref(null),s=t.ref(null),y=t.ref(null),b=t.ref(!1);function u(){return F(this,null,function*(){p.value=!0,m.value=null;try{const g=yield N({dataUrl:a,cacheTTL:r,cacheKey:d,useStaleCache:c});return s.value=g.data,m.value=g.error,y.value=g.source,b.value=g.isDummy,g.data}catch(g){return m.value=g instanceof Error?g:new Error("Failed to load data"),null}finally{p.value=!1}})}const i=t.computed(()=>{var g;return(g=s.value)!=null&&g.lastUpdated?D(s.value.lastUpdated):""}),S=t.computed(()=>{if(b.value)return"⚠️ Using dummy data for testing";switch(y.value){case"static":return"Real-time data";case"cache":return"Cached data";case"mock":return"Sample data";default:return""}});return u(),{data:s,loading:p,error:m,dataSource:y,dataSourceText:S,lastUpdatedText:i,isDummy:b,loadData:u}}const J={class:"git-contribution-graph"},K={class:"graph-header"},H={class:"header-info"},Q={class:"contribution-count"},X={key:0,class:"header-actions"},Z={key:0,class:"settings-dropdown"},tt=["onClick"],et={key:0,class:"loading-state"},ot={key:1,class:"graph-container"},at={class:"graph-content-wrapper"},nt={class:"months-row"},st={class:"months-container"},rt={class:"grid-container"},lt={class:"contribution-grid"},ct=["title","onClick"],it={class:"graph-footer"},dt={key:0,class:"last-updated"},mt=t.defineComponent({__name:"ContributionGraph",props:{dataUrl:{default:"/data/git-stats.json"},profileIndex:{default:0},colorScheme:{default:"green"},showSettings:{type:Boolean,default:!0},cacheTTL:{default:24*60*60*1e3}},emits:["day-click","color-scheme-change"],setup(o,{emit:a}){const r=o,c=a,{data:d,loading:p,dataSourceText:m,lastUpdatedText:s,isDummy:y}=M({dataUrl:r.dataUrl,cacheTTL:r.cacheTTL}),b=t.ref(r.colorScheme),u=t.ref(!1),i=["green","blue","purple","orange"],S=t.ref([]),g=t.ref([]);t.watch(d,e=>{var n,f,h;if((h=(f=(n=e==null?void 0:e.profiles)==null?void 0:n[r.profileIndex])==null?void 0:f.stats)!=null&&h.contributions){const E=e.profiles[r.profileIndex].stats.contributions;E&&(S.value=v(E),Ot())}else S.value=[]},{immediate:!0});const w=t.computed(()=>!S.value||S.value.length===0?0:S.value.reduce((e,n)=>!n.days||!Array.isArray(n.days)?e:e+n.days.reduce((f,h)=>f+(h.count||0),0),0));function v(e){if(!e||!Array.isArray(e))return C();const n=e.map(f=>({weekStart:f.firstDay||"",days:f.contributionDays.map(h=>{var E;return{date:h.date||"",count:(E=h.contributionCount)!=null?E:0,weekday:h.weekday||0}})}));for(;n.length<53;)n.push(B());return n}function C(){const e=[];for(let n=0;n<53;n++)e.push(B());return e}function B(){const e=[];for(let n=0;n<7;n++)e.push({date:"",count:0,weekday:n});return{weekStart:"",days:e}}function Ot(){if(!S.value||S.value.length===0){g.value=[];return}const e=[];let n=-1,f=-1;S.value.forEach((h,E)=>{if(!h.days||h.days.length===0)return;const j=h.days[0].date;if(!j)return;const U=j.split("-");if(U.length!==3)return;const[T,$]=U.map(Number);if(!(isNaN(T)||isNaN($))&&($!==n||T!==f)){const qt=new Date(T,$-1,1);e.push({week:E,month:$-1,year:T,label:qt.toLocaleDateString("en-US",{month:"short"})}),n=$,f=T}}),g.value=e}function Gt(e){return`level-${At(e)} ${b.value}`}function At(e){return e===0?0:e<=3?1:e<=6?2:e<=9?3:4}function Pt(e){if(!e.date)return"";const[n,f,h]=e.date.split("-").map(Number),j=new Date(n,f-1,h).toLocaleDateString("en-US",{weekday:"short",year:"numeric",month:"short",day:"numeric"}),U=e.count===1?"contribution":"contributions";return`${e.count} ${U} on ${j}`}function Yt(e){c("day-click",{date:e.date,count:e.count})}function Rt(){u.value=!u.value}function Wt(e){b.value=e,u.value=!1,c("color-scheme-change",e)}return(e,n)=>(t.openBlock(),t.createElementBlock("div",J,[t.createElementVNode("div",K,[t.createElementVNode("div",H,[t.createElementVNode("h5",Q,t.toDisplayString(w.value.toLocaleString())+" contributions in the last year ",1),t.createElementVNode("small",{class:t.normalizeClass(["data-source-text",{"is-dummy":t.unref(y)}])},t.toDisplayString(t.unref(m)),3)]),o.showSettings?(t.openBlock(),t.createElementBlock("div",X,[t.createElementVNode("button",{class:"settings-btn",type:"button",onClick:Rt},[t.renderSlot(e.$slots,"settings-icon",{},()=>[n[0]||(n[0]=t.createTextVNode("⚙️",-1))],!0),n[1]||(n[1]=t.createTextVNode(" Settings ",-1))]),u.value?(t.openBlock(),t.createElementBlock("div",Z,[(t.openBlock(),t.createElementBlock(t.Fragment,null,t.renderList(i,f=>t.createElementVNode("button",{key:f,onClick:h=>Wt(f),class:"settings-item"},t.toDisplayString(f)+" theme ",9,tt)),64))])):t.createCommentVNode("",!0)])):t.createCommentVNode("",!0)]),t.unref(p)?(t.openBlock(),t.createElementBlock("div",et,[...n[2]||(n[2]=[t.createElementVNode("div",{class:"spinner"},null,-1),t.createElementVNode("span",null,"Loading contributions...",-1)])])):(t.openBlock(),t.createElementBlock("div",ot,[t.createElementVNode("div",at,[t.createElementVNode("div",nt,[n[3]||(n[3]=t.createElementVNode("div",{class:"month-spacer"},null,-1)),t.createElementVNode("div",st,[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(g.value,f=>(t.openBlock(),t.createElementBlock("div",{key:`${f.year}-${f.month}`,class:"month-label",style:t.normalizeStyle({gridColumn:`${f.week+1} / span 1`})},t.toDisplayString(f.label),5))),128))])]),t.createElementVNode("div",rt,[n[4]||(n[4]=t.createStaticVNode('<div class="day-labels" data-v-f1ff100d><div class="day-label" data-v-f1ff100d>Mon</div><div class="day-label" data-v-f1ff100d></div><div class="day-label" data-v-f1ff100d>Wed</div><div class="day-label" data-v-f1ff100d></div><div class="day-label" data-v-f1ff100d>Fri</div><div class="day-label" data-v-f1ff100d></div><div class="day-label" data-v-f1ff100d></div></div>',1)),t.createElementVNode("div",lt,[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(S.value,f=>(t.openBlock(),t.createElementBlock("div",{key:f.weekStart,class:"contribution-week"},[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(f.days,h=>(t.openBlock(),t.createElementBlock("div",{key:h.date,class:t.normalizeClass(["contribution-day",Gt(h.count)]),title:Pt(h),onClick:E=>Yt(h)},null,10,ct))),128))]))),128))])])]),t.createElementVNode("div",it,[t.unref(s)?(t.openBlock(),t.createElementBlock("small",dt," Last updated: "+t.toDisplayString(t.unref(s)),1)):t.createCommentVNode("",!0),n[5]||(n[5]=t.createStaticVNode('<div class="legend" data-v-f1ff100d><small class="legend-label" data-v-f1ff100d>Less</small><div class="legend-squares" data-v-f1ff100d><div class="contribution-day level-0" data-v-f1ff100d></div><div class="contribution-day level-1" data-v-f1ff100d></div><div class="contribution-day level-2" data-v-f1ff100d></div><div class="contribution-day level-3" data-v-f1ff100d></div><div class="contribution-day level-4" data-v-f1ff100d></div></div><small class="legend-label" data-v-f1ff100d>More</small></div>',1))])]))]))}}),G=(o,a)=>{const r=o.__vccOpts||o;for(const[c,d]of a)r[c]=d;return r},A=G(mt,[["__scopeId","data-v-f1ff100d"]]),ut={class:"git-stats-breakdown"},ft={class:"stats-grid"},pt={class:"stat-card"},yt={class:"stat-icon"},ht={class:"stat-content"},gt={class:"stat-value"},kt={class:"stat-card"},St={class:"stat-icon"},bt={class:"stat-content"},Dt={key:0,class:"stat-loading"},Et={key:1,class:"stat-value"},_t={class:"stat-card"},Ct={class:"stat-icon"},Nt={class:"stat-content"},wt={key:0,class:"stat-loading"},Vt={key:1,class:"stat-value"},Bt={key:0,class:"stat-card"},Tt={class:"stat-icon"},$t={class:"stat-content"},Lt={key:0,class:"stat-loading"},jt={key:1,class:"stat-value"},Ut={class:"stat-label"},Ft={class:"stats-footer"},xt={key:0,class:"data-source"},It={key:0},Mt=t.defineComponent({__name:"StatsBreakdown",props:{dataUrl:{default:"/data/git-stats.json"},profileIndexes:{default:()=>[]},experienceData:{default:()=>[]},showCustomStat:{type:Boolean,default:!0},customStatCalculator:{type:[Function,null],default:null},cacheTTL:{default:24*60*60*1e3}},setup(o){const a=o,{data:r,loading:c,dataSourceText:d,lastUpdatedText:p}=M({dataUrl:a.dataUrl,cacheTTL:a.cacheTTL}),m=t.ref(0),s=t.ref(0);t.watch(r,u=>{var i,S;if(u)if(a.profileIndexes.length>0){let g=0,w=0;a.profileIndexes.forEach(v=>{var B;const C=(B=u.profiles)==null?void 0:B[v];C!=null&&C.stats&&(g+=C.stats.projectCount||0,w+=C.stats.commitCount||0)}),m.value=g,s.value=w}else m.value=((i=u.totals)==null?void 0:i.projectCount)||0,s.value=((S=u.totals)==null?void 0:S.commitCount)||0},{immediate:!0});const y=t.computed(()=>O(a.experienceData).toFixed(1)),b=t.computed(()=>{if(a.customStatCalculator){const w={projects:m.value,commits:s.value,years:parseFloat(y.value)};return a.customStatCalculator(w)}return(m.value*1.5+s.value*1.2+parseFloat(y.value)*1.5).toFixed(2)});return(u,i)=>(t.openBlock(),t.createElementBlock("div",ut,[t.createElementVNode("div",ft,[t.createElementVNode("div",pt,[t.createElementVNode("div",yt,[t.renderSlot(u.$slots,"icon-experience",{},()=>[i[0]||(i[0]=t.createTextVNode("⏱️",-1))],!0)]),t.createElementVNode("div",ht,[t.createElementVNode("div",gt,t.toDisplayString(y.value),1),i[1]||(i[1]=t.createElementVNode("div",{class:"stat-label"},"Years Experience",-1))])]),t.createElementVNode("div",kt,[t.createElementVNode("div",St,[t.renderSlot(u.$slots,"icon-projects",{},()=>[i[2]||(i[2]=t.createTextVNode("📦",-1))],!0)]),t.createElementVNode("div",bt,[t.unref(c)?(t.openBlock(),t.createElementBlock("div",Dt,[...i[3]||(i[3]=[t.createElementVNode("div",{class:"spinner"},null,-1)])])):(t.openBlock(),t.createElementBlock("div",Et,t.toDisplayString(m.value),1)),i[4]||(i[4]=t.createElementVNode("div",{class:"stat-label"},"Projects",-1))])]),t.createElementVNode("div",_t,[t.createElementVNode("div",Ct,[t.renderSlot(u.$slots,"icon-commits",{},()=>[i[5]||(i[5]=t.createTextVNode("💻",-1))],!0)]),t.createElementVNode("div",Nt,[t.unref(c)?(t.openBlock(),t.createElementBlock("div",wt,[...i[6]||(i[6]=[t.createElementVNode("div",{class:"spinner"},null,-1)])])):(t.openBlock(),t.createElementBlock("div",Vt,t.toDisplayString(s.value),1)),i[7]||(i[7]=t.createElementVNode("div",{class:"stat-label"},"Commits",-1))])]),o.showCustomStat?(t.openBlock(),t.createElementBlock("div",Bt,[t.createElementVNode("div",Tt,[t.renderSlot(u.$slots,"icon-custom",{},()=>[i[8]||(i[8]=t.createTextVNode("☕",-1))],!0)]),t.createElementVNode("div",$t,[t.unref(c)?(t.openBlock(),t.createElementBlock("div",Lt,[...i[9]||(i[9]=[t.createElementVNode("div",{class:"spinner"},null,-1)])])):(t.openBlock(),t.createElementBlock("div",jt,t.toDisplayString(b.value),1)),t.createElementVNode("div",Ut,[t.renderSlot(u.$slots,"custom-stat-label",{},()=>[i[10]||(i[10]=t.createTextVNode("Coffee Consumed",-1))],!0)])])])):t.createCommentVNode("",!0)]),t.createElementVNode("div",Ft,[t.unref(d)?(t.openBlock(),t.createElementBlock("small",xt,[t.createTextVNode(t.toDisplayString(t.unref(d))+" ",1),t.unref(p)?(t.openBlock(),t.createElementBlock("span",It," · "+t.toDisplayString(t.unref(p)),1)):t.createCommentVNode("",!0)])):t.createCommentVNode("",!0)])]))}}),P=G(Mt,[["__scopeId","data-v-97689125"]]),vt={install(o){o.component("ContributionGraph",A),o.component("StatsBreakdown",P)}};l.ContributionGraph=A,l.StatsBreakdown=P,l.calculateYearsExperience=O,l.default=vt,l.fetchGitStats=N,l.fetchGitStatsAPI=k,l.formatLastUpdated=D,l.generateDummyContributions=L,l.generateDummyStats=_,l.generateMultiProfileDummyStats=x,l.getContributionLevel=V,l.saveDummyDataToFile=I,l.useGitStats=M,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
2
|
+
//# sourceMappingURL=vue-git-stats.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-git-stats.umd.js","sources":["../../core/src/api/fetchGitStats.ts","../../core/src/utils/generateDummyData.js","../../core/src/index.ts","../src/composables/useGitStats.ts","../src/components/ContributionGraph.vue","../src/components/StatsBreakdown.vue","../src/index.ts"],"sourcesContent":["import type { GitStatsData } from '../types'\r\n\r\nexport async function fetchGitStats(url: string): Promise<GitStatsData> {\r\n\tconst response = await fetch(url)\r\n\tif (!response.ok) {\r\n\t\tthrow new Error(`Failed to fetch git stats: ${response.statusText}`)\r\n\t}\r\n\treturn response.json()\r\n}\r\n","/**\n * Generate realistic dummy data for testing and development\n */\n\n/**\n * Generate dummy contribution data (53 weeks)\n */\nexport function generateDummyContributions() {\n\tconst weeks = []\n\tconst now = new Date()\n\n\t// Get the Sunday that starts the week containing today\n\tconst endDate = new Date(now)\n\tendDate.setDate(endDate.getDate() - endDate.getDay())\n\n\t// Go back exactly 52 weeks\n\tconst startDate = new Date(endDate)\n\tstartDate.setDate(startDate.getDate() - 52 * 7)\n\n\tconst currentDate = new Date(startDate)\n\n\tfor (let week = 0; week < 53; week++) {\n\t\tconst weekData = {\n\t\t\tweekStart: new Date(currentDate).toISOString().split('T')[0],\n\t\t\tcontributionDays: [],\n\t\t}\n\n\t\tfor (let day = 0; day < 7; day++) {\n\t\t\tconst isInFuture = currentDate > now\n\t\t\tconst isWeekend = day === 0 || day === 6\n\n\t\t\t// More realistic pattern: fewer commits on weekends, none in future\n\t\t\tlet dayCount = 0\n\t\t\tif (!isInFuture) {\n\t\t\t\tif (isWeekend) {\n\t\t\t\t\tdayCount =\n\t\t\t\t\t\tMath.random() < 0.3 ? Math.floor(Math.random() * 5) : 0\n\t\t\t\t} else {\n\t\t\t\t\tdayCount = Math.floor(Math.random() * 15)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tweekData.contributionDays.push({\n\t\t\t\tdate: new Date(currentDate).toISOString().split('T')[0],\n\t\t\t\tcontributionCount: dayCount,\n\t\t\t\tweekday: day,\n\t\t\t})\n\t\t\tcurrentDate.setDate(currentDate.getDate() + 1)\n\t\t}\n\n\t\tweeks.push(weekData)\n\t}\n\n\treturn weeks\n}\n\n/**\n * Generate complete dummy stats data\n */\nexport function generateDummyStats(options = {}) {\n\tconst {\n\t\tusername = 'demo-user',\n\t\tplatform = 'github',\n\t\tprojectCount = 30,\n\t\tcommitCount = 2500,\n\t} = options\n\n\tconst contributions = generateDummyContributions()\n\tconst totalContributions = contributions.reduce((total, week) => {\n\t\treturn (\n\t\t\ttotal +\n\t\t\tweek.contributionDays.reduce(\n\t\t\t\t(sum, day) => sum + day.contributionCount,\n\t\t\t\t0\n\t\t\t)\n\t\t)\n\t}, 0)\n\n\treturn {\n\t\tlastUpdated: new Date().toISOString(),\n\t\tprofiles: [\n\t\t\t{\n\t\t\t\tusername,\n\t\t\t\tplatform,\n\t\t\t\tstats: {\n\t\t\t\t\tprojectCount,\n\t\t\t\t\tcommitCount,\n\t\t\t\t\tcontributions: contributions.map((week) => ({\n\t\t\t\t\t\tfirstDay: week.weekStart,\n\t\t\t\t\t\tcontributionDays: week.contributionDays,\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\ttotals: {\n\t\t\tprojectCount,\n\t\t\tcommitCount,\n\t\t},\n\t\tmetadata: {\n\t\t\tfetchedAt: Date.now(),\n\t\t\tsource: 'dummy_data',\n\t\t\tisDummy: true,\n\t\t},\n\t}\n}\n\n/**\n * Generate multiple profiles dummy data\n */\nexport function generateMultiProfileDummyStats() {\n\tconst githubProfile = generateDummyStats({\n\t\tusername: 'demo-github',\n\t\tplatform: 'github',\n\t\tprojectCount: 45,\n\t\tcommitCount: 2847,\n\t})\n\n\tconst gitlabProfile = generateDummyStats({\n\t\tusername: 'demo-gitlab',\n\t\tplatform: 'gitlab',\n\t\tprojectCount: 7,\n\t\tcommitCount: 523,\n\t})\n\n\treturn {\n\t\tlastUpdated: new Date().toISOString(),\n\t\tprofiles: [githubProfile.profiles[0], gitlabProfile.profiles[0]],\n\t\ttotals: {\n\t\t\tprojectCount: 45 + 7,\n\t\t\tcommitCount: 2847 + 523,\n\t\t},\n\t\tmetadata: {\n\t\t\tfetchedAt: Date.now(),\n\t\t\tsource: 'dummy_data',\n\t\t\tisDummy: true,\n\t\t},\n\t}\n}\n\n/**\n * Save dummy data to a file (for testing)\n */\nexport function saveDummyDataToFile(filepath = 'dummy-git-stats.json') {\n\tconst data = generateDummyStats()\n\tconst json = JSON.stringify(data, null, '\\t')\n\n\tif (typeof window !== 'undefined') {\n\t\t// Browser environment - trigger download\n\t\tconst blob = new Blob([json], { type: 'application/json' })\n\t\tconst url = URL.createObjectURL(blob)\n\t\tconst a = document.createElement('a')\n\t\ta.href = url\n\t\ta.download = filepath\n\t\ta.click()\n\t\tURL.revokeObjectURL(url)\n\t} else {\n\t\t// Node environment\n\t\ttry {\n\t\t\tconst fs = require('fs')\n\t\t\tfs.writeFileSync(filepath, json)\n\t\t\tconsole.log(`✓ Dummy data saved to ${filepath}`)\n\t\t} catch (err) {\n\t\t\tconsole.error('Failed to save dummy data:', err)\n\t\t}\n\t}\n}\n","// packages/core/src/index.ts\n// Framework-agnostic core logic\n\n// Re-export everything from types\nexport type {\n\tColorScheme,\n\tContributionDay,\n\tContributionWeek,\n\tProfile,\n\tGitStatsData,\n\tExperienceEntry,\n\tDataSource,\n\tPlatform,\n\tProfileStats,\n\tStatsTotals,\n\tStatsMetadata,\n\tCustomStatCalculator,\n\tCustomStatCalculatorParams,\n} from './types/index.js'\n\n// Export API functions\nexport { fetchGitStats as fetchGitStatsAPI } from './api/fetchGitStats.js'\n\n// Export utility functions with explicit imports\nexport {\n\tgenerateDummyStats,\n\tgenerateDummyContributions,\n\tgenerateMultiProfileDummyStats,\n\tsaveDummyDataToFile,\n} from './utils/generateDummyData.js'\n\n// Core data fetching (framework-agnostic)\nimport type { GitStatsData } from './types/index.js'\n\nexport interface FetchOptions {\n\tdataUrl: string\n\tcacheTTL?: number\n\tcacheKey?: string\n\tuseStaleCache?: boolean\n}\n\nexport interface DataResult<T> {\n\tdata: T | null\n\terror: Error | null\n\tsource: 'static' | 'cache' | 'mock' | 'dummy' | null\n\tisDummy: boolean\n}\n\n/**\n * Framework-agnostic data fetcher\n */\nexport async function fetchGitStats(\n\toptions: FetchOptions\n): Promise<DataResult<GitStatsData>> {\n\tconst { dataUrl, cacheKey = 'git_stats_cache', useStaleCache = true } = options\n\n\ttry {\n\t\t// Try static file first\n\t\tconst response = await fetch(dataUrl)\n\t\tif (response.ok) {\n\t\t\tconst data = await response.json()\n\t\t\t// Cache the data\n\t\t\tif (typeof window !== 'undefined') {\n\t\t\t\tlocalStorage.setItem(\n\t\t\t\t\tcacheKey,\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t...data,\n\t\t\t\t\t\tcachedAt: Date.now(),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tdata,\n\t\t\t\terror: null,\n\t\t\t\tsource: 'static',\n\t\t\t\tisDummy: data.metadata?.isDummy === true,\n\t\t\t}\n\t\t}\n\t} catch (err) {\n\t\tconsole.warn('Failed to fetch from static file:', err)\n\t}\n\n\t// Try cache\n\tif (useStaleCache && typeof window !== 'undefined') {\n\t\ttry {\n\t\t\tconst cached = localStorage.getItem(cacheKey)\n\t\t\tif (cached) {\n\t\t\t\tconst data = JSON.parse(cached)\n\t\t\t\treturn {\n\t\t\t\t\tdata,\n\t\t\t\t\terror: null,\n\t\t\t\t\tsource: 'cache',\n\t\t\t\t\tisDummy: data.metadata?.isDummy === true,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.warn('Failed to load from cache:', err)\n\t\t}\n\t}\n\n\t// Fallback to mock\n\tconst mockData = generateMockData()\n\treturn {\n\t\tdata: mockData,\n\t\terror: null,\n\t\tsource: 'mock',\n\t\tisDummy: false,\n\t}\n}\n\n/**\n * Format last updated time\n */\nexport function formatLastUpdated(dateString: string): string {\n\tconst date = new Date(dateString)\n\tconst now = new Date()\n\tconst diffHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60))\n\n\tif (diffHours < 1) return 'just now'\n\tif (diffHours < 24) return `${diffHours} hours ago`\n\n\tconst diffDays = Math.floor(diffHours / 24)\n\tif (diffDays === 1) return 'yesterday'\n\tif (diffDays < 7) return `${diffDays} days ago`\n\n\treturn date.toLocaleDateString('en-US', {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,\n\t})\n}\n\n/**\n * Get contribution level (0-4)\n */\nexport function getContributionLevel(count: number): number {\n\tif (count === 0) return 0\n\tif (count <= 3) return 1\n\tif (count <= 6) return 2\n\tif (count <= 9) return 3\n\treturn 4\n}\n\n/**\n * Calculate years of experience\n */\nexport function calculateYearsExperience(\n\texperienceData: { startDate: string; endDate: string | null; skills?: string[] }[]\n): number {\n\tif (experienceData.length === 0) return 0\n\n\tconst skillExperience: Record<string, number> = {}\n\n\texperienceData.forEach((exp) => {\n\t\tconst end = exp.endDate ? new Date(exp.endDate) : new Date()\n\t\tconst start = new Date(exp.startDate)\n\t\tconst years = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24 * 365.25)\n\n\t\texp.skills?.forEach((skill) => {\n\t\t\tif (!skillExperience[skill]) {\n\t\t\t\tskillExperience[skill] = 0\n\t\t\t}\n\t\t\tskillExperience[skill] += years\n\t\t})\n\t})\n\n\treturn Math.max(...Object.values(skillExperience), 0)\n}\n\n// Mock data generator\nfunction generateMockData(): GitStatsData {\n\treturn {\n\t\tlastUpdated: new Date().toISOString(),\n\t\tprofiles: [\n\t\t\t{\n\t\t\t\tusername: 'mockuser',\n\t\t\t\tplatform: 'github',\n\t\t\t\tstats: {\n\t\t\t\t\tprojectCount: 30,\n\t\t\t\t\tcommitCount: 2500,\n\t\t\t\t\tcontributions: [],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\ttotals: {\n\t\t\tprojectCount: 30,\n\t\t\tcommitCount: 2500,\n\t\t},\n\t\tmetadata: {\n\t\t\tsource: 'mock',\n\t\t\tfetchedAt: Date.now(),\n\t\t},\n\t}\n}","import { ref, computed } from 'vue'\r\nimport {\r\n\tfetchGitStats,\r\n\tformatLastUpdated,\r\n\ttype GitStatsData,\r\n\ttype DataSource,\r\n} from '@git-stats-components/core'\r\n\r\nexport interface UseGitStatsConfig {\r\n\tdataUrl?: string\r\n\tcacheTTL?: number\r\n\tuseStaleCache?: boolean\r\n\tcacheKey?: string\r\n}\r\n\r\nexport function useGitStats(config: UseGitStatsConfig = {}) {\r\n\tconst {\r\n\t\tdataUrl = '/data/git-stats.json',\r\n\t\tcacheTTL = 24 * 60 * 60 * 1000,\r\n\t\tuseStaleCache = true,\r\n\t\tcacheKey = 'git_stats_cache',\r\n\t} = config\r\n\r\n\tconst loading = ref(false)\r\n\tconst error = ref<Error | null>(null)\r\n\tconst data = ref<GitStatsData | null>(null)\r\n\tconst dataSource = ref<DataSource | null>(null)\r\n\tconst isDummy = ref(false)\r\n\r\n\t/**\r\n\t * Load data with fallback strategy\r\n\t */\r\n\tasync function loadData(): Promise<GitStatsData | null> {\r\n\t\tloading.value = true\r\n\t\terror.value = null\r\n\r\n\t\ttry {\r\n\t\t\tconst result = await fetchGitStats({\r\n\t\t\t\tdataUrl,\r\n\t\t\t\tcacheTTL,\r\n\t\t\t\tcacheKey,\r\n\t\t\t\tuseStaleCache,\r\n\t\t\t})\r\n\r\n\t\t\tdata.value = result.data\r\n\t\t\terror.value = result.error\r\n\t\t\tdataSource.value = result.source\r\n\t\t\tisDummy.value = result.isDummy\r\n\r\n\t\t\treturn result.data\r\n\t\t} catch (err) {\r\n\t\t\terror.value =\r\n\t\t\t\terr instanceof Error ? err : new Error('Failed to load data')\r\n\t\t\treturn null\r\n\t\t} finally {\r\n\t\t\tloading.value = false\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Format \"last updated\" text\r\n\t */\r\n\tconst lastUpdatedText = computed(() => {\r\n\t\tif (!data.value?.lastUpdated) return ''\r\n\t\treturn formatLastUpdated(data.value.lastUpdated)\r\n\t})\r\n\r\n\t/**\r\n\t * Computed data source display text\r\n\t */\r\n\tconst dataSourceText = computed(() => {\r\n\t\tif (isDummy.value) {\r\n\t\t\treturn '⚠️ Using dummy data for testing'\r\n\t\t}\r\n\r\n\t\tswitch (dataSource.value) {\r\n\t\t\tcase 'static':\r\n\t\t\t\treturn 'Real-time data'\r\n\t\t\tcase 'cache':\r\n\t\t\t\treturn 'Cached data'\r\n\t\t\tcase 'mock':\r\n\t\t\t\treturn 'Sample data'\r\n\t\t\tdefault:\r\n\t\t\t\treturn ''\r\n\t\t}\r\n\t})\r\n\r\n\t// Auto-load on creation\r\n\tloadData()\r\n\r\n\treturn {\r\n\t\tdata,\r\n\t\tloading,\r\n\t\terror,\r\n\t\tdataSource,\r\n\t\tdataSourceText,\r\n\t\tlastUpdatedText,\r\n\t\tisDummy,\r\n\t\tloadData,\r\n\t}\r\n}\r\n","<template>\r\n\t<div class=\"git-contribution-graph\">\r\n\t\t<!-- Header -->\r\n\t\t<div class=\"graph-header\">\r\n\t\t\t<div class=\"header-info\">\r\n\t\t\t\t<h5 class=\"contribution-count\">\r\n\t\t\t\t\t{{ totalContributions.toLocaleString() }} contributions in\r\n\t\t\t\t\tthe last year\r\n\t\t\t\t</h5>\r\n\t\t\t\t<small\r\n\t\t\t\t\tclass=\"data-source-text\"\r\n\t\t\t\t\t:class=\"{ 'is-dummy': isDummy }\"\r\n\t\t\t\t>\r\n\t\t\t\t\t{{ dataSourceText }}\r\n\t\t\t\t</small>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"header-actions\" v-if=\"showSettings\">\r\n\t\t\t\t<button\r\n\t\t\t\t\tclass=\"settings-btn\"\r\n\t\t\t\t\ttype=\"button\"\r\n\t\t\t\t\t@click=\"toggleSettings\"\r\n\t\t\t\t>\r\n\t\t\t\t\t<slot name=\"settings-icon\">⚙️</slot>\r\n\t\t\t\t\tSettings\r\n\t\t\t\t</button>\r\n\t\t\t\t<div v-if=\"settingsOpen\" class=\"settings-dropdown\">\r\n\t\t\t\t\t<button\r\n\t\t\t\t\t\tv-for=\"scheme in colorSchemes\"\r\n\t\t\t\t\t\t:key=\"scheme\"\r\n\t\t\t\t\t\t@click=\"changeColorScheme(scheme)\"\r\n\t\t\t\t\t\tclass=\"settings-item\"\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t{{ scheme }} theme\r\n\t\t\t\t\t</button>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\r\n\t\t<!-- Loading state -->\r\n\t\t<div v-if=\"loading\" class=\"loading-state\">\r\n\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t<span>Loading contributions...</span>\r\n\t\t</div>\r\n\r\n\t\t<!-- Contribution grid -->\r\n\t\t<div v-else class=\"graph-container\">\r\n\t\t\t<div class=\"graph-content-wrapper\">\r\n\t\t\t\t<!-- Month labels -->\r\n\t\t\t\t<div class=\"months-row\">\r\n\t\t\t\t\t<div class=\"month-spacer\"></div>\r\n\t\t\t\t\t<div class=\"months-container\">\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tv-for=\"month in monthLabels\"\r\n\t\t\t\t\t\t\t:key=\"`${month.year}-${month.month}`\"\r\n\t\t\t\t\t\t\tclass=\"month-label\"\r\n\t\t\t\t\t\t\t:style=\"{\r\n\t\t\t\t\t\t\t\tgridColumn: `${month.week + 1} / span 1`,\r\n\t\t\t\t\t\t\t}\"\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t{{ month.label }}\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\r\n\t\t\t\t<!-- Grid with day labels -->\r\n\t\t\t\t<div class=\"grid-container\">\r\n\t\t\t\t\t<!-- Day labels -->\r\n\t\t\t\t\t<div class=\"day-labels\">\r\n\t\t\t\t\t\t<div class=\"day-label\">Mon</div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t\t<div class=\"day-label\">Wed</div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t\t<div class=\"day-label\">Fri</div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t\t<div class=\"day-label\"></div>\r\n\t\t\t\t\t</div>\r\n\r\n\t\t\t\t\t<!-- Contribution squares -->\r\n\t\t\t\t\t<div class=\"contribution-grid\">\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tv-for=\"week in contributionData\"\r\n\t\t\t\t\t\t\t:key=\"week.weekStart\"\r\n\t\t\t\t\t\t\tclass=\"contribution-week\"\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\t\tv-for=\"day in week.days\"\r\n\t\t\t\t\t\t\t\t:key=\"day.date\"\r\n\t\t\t\t\t\t\t\tclass=\"contribution-day\"\r\n\t\t\t\t\t\t\t\t:class=\"getContributionLevel(day.count)\"\r\n\t\t\t\t\t\t\t\t:title=\"getTooltipText(day)\"\r\n\t\t\t\t\t\t\t\t@click=\"onDayClick(day)\"\r\n\t\t\t\t\t\t\t></div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Legend -->\r\n\t\t\t<div class=\"graph-footer\">\r\n\t\t\t\t<small class=\"last-updated\" v-if=\"lastUpdatedText\">\r\n\t\t\t\t\tLast updated: {{ lastUpdatedText }}\r\n\t\t\t\t</small>\r\n\t\t\t\t<div class=\"legend\">\r\n\t\t\t\t\t<small class=\"legend-label\">Less</small>\r\n\t\t\t\t\t<div class=\"legend-squares\">\r\n\t\t\t\t\t\t<div class=\"contribution-day level-0\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-1\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-2\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-3\"></div>\r\n\t\t\t\t\t\t<div class=\"contribution-day level-4\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<small class=\"legend-label\">More</small>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport { useGitStats } from '../composables/useGitStats'\r\nimport {\r\n\ttype ColorScheme,\r\n\ttype ContributionWeek,\r\n} from '@git-stats-components/core'\r\n\r\ninterface ProcessedWeek {\r\n\tweekStart: string\r\n\tdays: ProcessedDay[]\r\n}\r\n\r\ninterface ProcessedDay {\r\n\tdate: string\r\n\tcount: number\r\n\tweekday: number\r\n}\r\n\r\ninterface MonthLabel {\r\n\tweek: number\r\n\tmonth: number\r\n\tyear: number\r\n\tlabel: string\r\n}\r\n\r\ninterface Props {\r\n\tdataUrl?: string\r\n\tprofileIndex?: number\r\n\tcolorScheme?: ColorScheme\r\n\tshowSettings?: boolean\r\n\tcacheTTL?: number\r\n}\r\n\r\ninterface Emits {\r\n\t(e: 'day-click', data: { date: string; count: number }): void\r\n\t(e: 'color-scheme-change', scheme: ColorScheme): void\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n\tdataUrl: '/data/git-stats.json',\r\n\tprofileIndex: 0,\r\n\tcolorScheme: 'green',\r\n\tshowSettings: true,\r\n\tcacheTTL: 24 * 60 * 60 * 1000,\r\n})\r\n\r\nconst emit = defineEmits<Emits>()\r\n\r\n// Use the shared composable\r\nconst { data, loading, dataSourceText, lastUpdatedText, isDummy } = useGitStats(\r\n\t{\r\n\t\tdataUrl: props.dataUrl,\r\n\t\tcacheTTL: props.cacheTTL,\r\n\t}\r\n)\r\n\r\nconst currentColorScheme = ref<ColorScheme>(props.colorScheme)\r\nconst settingsOpen = ref(false)\r\nconst colorSchemes: ColorScheme[] = ['green', 'blue', 'purple', 'orange']\r\nconst contributionData = ref<ProcessedWeek[]>([])\r\nconst monthLabels = ref<MonthLabel[]>([])\r\n\r\n// Process contribution data when loaded\r\nwatch(\r\n\tdata,\r\n\t(newData) => {\r\n\t\tif (newData?.profiles?.[props.profileIndex]?.stats?.contributions) {\r\n\t\t\tconst contributions =\r\n\t\t\t\tnewData.profiles[props.profileIndex].stats.contributions\r\n\t\t\tif (contributions) {\r\n\t\t\t\tcontributionData.value = processContributions(contributions)\r\n\t\t\t\tgenerateMonthLabels()\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tcontributionData.value = []\r\n\t\t}\r\n\t},\r\n\t{ immediate: true }\r\n)\r\n\r\nconst totalContributions = computed(() => {\r\n\tif (!contributionData.value || contributionData.value.length === 0) {\r\n\t\treturn 0\r\n\t}\r\n\r\n\treturn contributionData.value.reduce((total, week) => {\r\n\t\tif (!week.days || !Array.isArray(week.days)) {\r\n\t\t\treturn total\r\n\t\t}\r\n\t\treturn (\r\n\t\t\ttotal +\r\n\t\t\tweek.days.reduce((weekTotal, day) => {\r\n\t\t\t\treturn weekTotal + (day.count || 0)\r\n\t\t\t}, 0)\r\n\t\t)\r\n\t}, 0)\r\n})\r\n\r\nfunction processContributions(\r\n\tcontributions: ContributionWeek[]\r\n): ProcessedWeek[] {\r\n\tif (!contributions || !Array.isArray(contributions)) {\r\n\t\treturn generateEmptyWeeks()\r\n\t}\r\n\r\n\tconst weeks = contributions.map((week) => ({\r\n\t\tweekStart: week.firstDay || '',\r\n\t\tdays: week.contributionDays.map((day) => ({\r\n\t\t\tdate: day.date || '',\r\n\t\t\tcount: day.contributionCount ?? 0,\r\n\t\t\tweekday: day.weekday || 0,\r\n\t\t})),\r\n\t}))\r\n\r\n\twhile (weeks.length < 53) {\r\n\t\tweeks.push(generateEmptyWeek())\r\n\t}\r\n\r\n\treturn weeks\r\n}\r\n\r\nfunction generateEmptyWeeks(): ProcessedWeek[] {\r\n\tconst weeks: ProcessedWeek[] = []\r\n\tfor (let i = 0; i < 53; i++) {\r\n\t\tweeks.push(generateEmptyWeek())\r\n\t}\r\n\treturn weeks\r\n}\r\n\r\nfunction generateEmptyWeek(): ProcessedWeek {\r\n\tconst days: ProcessedDay[] = []\r\n\tfor (let i = 0; i < 7; i++) {\r\n\t\tdays.push({ date: '', count: 0, weekday: i })\r\n\t}\r\n\treturn { weekStart: '', days }\r\n}\r\n\r\nfunction generateMonthLabels(): void {\r\n\tif (!contributionData.value || contributionData.value.length === 0) {\r\n\t\tmonthLabels.value = []\r\n\t\treturn\r\n\t}\r\n\r\n\tconst monthPositions: MonthLabel[] = []\r\n\tlet lastMonth = -1\r\n\tlet lastYear = -1\r\n\r\n\tcontributionData.value.forEach((week, weekIndex) => {\r\n\t\tif (!week.days || week.days.length === 0) return\r\n\r\n\t\tconst firstDay = week.days[0].date\r\n\t\tif (!firstDay) return\r\n\r\n\t\tconst dateParts = firstDay.split('-')\r\n\t\tif (dateParts.length !== 3) return\r\n\r\n\t\tconst [year, month] = dateParts.map(Number)\r\n\t\tif (isNaN(year) || isNaN(month)) return\r\n\r\n\t\tif (month !== lastMonth || year !== lastYear) {\r\n\t\t\tconst date = new Date(year, month - 1, 1)\r\n\t\t\tmonthPositions.push({\r\n\t\t\t\tweek: weekIndex,\r\n\t\t\t\tmonth: month - 1,\r\n\t\t\t\tyear: year,\r\n\t\t\t\tlabel: date.toLocaleDateString('en-US', { month: 'short' }),\r\n\t\t\t})\r\n\t\t\tlastMonth = month\r\n\t\t\tlastYear = year\r\n\t\t}\r\n\t})\r\n\r\n\tmonthLabels.value = monthPositions\r\n}\r\n\r\nfunction getContributionLevel(count: number): string {\r\n\tconst level = getContributionLevelNumber(count)\r\n\treturn `level-${level} ${currentColorScheme.value}`\r\n}\r\n\r\nfunction getContributionLevelNumber(count: number): number {\r\n\tif (count === 0) return 0\r\n\tif (count <= 3) return 1\r\n\tif (count <= 6) return 2\r\n\tif (count <= 9) return 3\r\n\treturn 4\r\n}\r\n\r\nfunction getTooltipText(day: ProcessedDay): string {\r\n\tif (!day.date) return ''\r\n\r\n\tconst [year, month, dayNum] = day.date.split('-').map(Number)\r\n\tconst date = new Date(year, month - 1, dayNum)\r\n\r\n\tconst formattedDate = date.toLocaleDateString('en-US', {\r\n\t\tweekday: 'short',\r\n\t\tyear: 'numeric',\r\n\t\tmonth: 'short',\r\n\t\tday: 'numeric',\r\n\t})\r\n\r\n\tconst contributionText = day.count === 1 ? 'contribution' : 'contributions'\r\n\treturn `${day.count} ${contributionText} on ${formattedDate}`\r\n}\r\n\r\nfunction onDayClick(day: ProcessedDay): void {\r\n\temit('day-click', { date: day.date, count: day.count })\r\n}\r\n\r\nfunction toggleSettings(): void {\r\n\tsettingsOpen.value = !settingsOpen.value\r\n}\r\n\r\nfunction changeColorScheme(scheme: ColorScheme): void {\r\n\tcurrentColorScheme.value = scheme\r\n\tsettingsOpen.value = false\r\n\temit('color-scheme-change', scheme)\r\n}\r\n</script>\r\n\r\n<style scoped>\r\n.graph-content-wrapper {\r\n\tjustify-items: anchor-center;\r\n}\r\n.git-contribution-graph {\r\n\tfont-family:\r\n\t\t-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,\r\n\t\tArial, sans-serif;\r\n\tfont-size: 12px;\r\n\tbackground: transparent;\r\n\tcolor: #e6edf3;\r\n\tpadding: 16px;\r\n\tmax-width: 1200px;\r\n\tmargin: 0 auto;\r\n\twidth: 100%;\r\n}\r\n\r\n.graph-header {\r\n\tdisplay: flex;\r\n\tjustify-content: space-between;\r\n\talign-items: center;\r\n\tmargin-bottom: 16px;\r\n}\r\n\r\n.contribution-count {\r\n\tmargin: 0 0 4px 0;\r\n\tfont-size: 16px;\r\n\tfont-weight: 600;\r\n}\r\n\r\n.data-source-text {\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.data-source-text.is-dummy {\r\n\tcolor: #f85149;\r\n\tfont-weight: 600;\r\n\tbackground: rgba(248, 81, 73, 0.1);\r\n\tpadding: 2px 8px;\r\n\tborder-radius: 4px;\r\n}\r\n\r\n.settings-btn {\r\n\tbackground: transparent;\r\n\tborder: 1px solid #30363d;\r\n\tcolor: #7d8590;\r\n\tpadding: 6px 12px;\r\n\tborder-radius: 6px;\r\n\tcursor: pointer;\r\n\tfont-size: 12px;\r\n}\r\n\r\n.settings-btn:hover {\r\n\tbackground: #21262d;\r\n\tcolor: #e6edf3;\r\n}\r\n\r\n.header-actions {\r\n\tposition: relative;\r\n}\r\n\r\n.settings-dropdown {\r\n\tposition: absolute;\r\n\tright: 0;\r\n\ttop: 100%;\r\n\tmargin-top: 4px;\r\n\tbackground: #21262d;\r\n\tborder: 1px solid #30363d;\r\n\tborder-radius: 6px;\r\n\tpadding: 4px;\r\n\tz-index: 10;\r\n\tmin-width: 150px;\r\n}\r\n\r\n.settings-item {\r\n\tdisplay: block;\r\n\twidth: 100%;\r\n\tbackground: transparent;\r\n\tborder: none;\r\n\tcolor: #e6edf3;\r\n\tpadding: 8px 12px;\r\n\ttext-align: left;\r\n\tcursor: pointer;\r\n\tborder-radius: 4px;\r\n}\r\n\r\n.settings-item:hover {\r\n\tbackground: #30363d;\r\n}\r\n\r\n.loading-state {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tjustify-content: center;\r\n\tgap: 12px;\r\n\tpadding: 40px;\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.spinner {\r\n\twidth: 20px;\r\n\theight: 20px;\r\n\tborder: 2px solid #30363d;\r\n\tborder-top-color: #58a6ff;\r\n\tborder-radius: 50%;\r\n\tanimation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n\tto {\r\n\t\ttransform: rotate(360deg);\r\n\t}\r\n}\r\n\r\n.months-row {\r\n\tdisplay: flex;\r\n\tmargin-bottom: 4px;\r\n}\r\n\r\n.month-spacer {\r\n\twidth: 27px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.months-container {\r\n\tdisplay: grid;\r\n\tgrid-template-columns: repeat(53, 11px);\r\n\tgap: 2px;\r\n\tflex: 1;\r\n\tmargin-left: 3px;\r\n\tmin-width: 0;\r\n}\r\n\r\n.month-label {\r\n\tfont-size: 11px;\r\n\tcolor: #7d8590;\r\n\ttext-align: left;\r\n}\r\n\r\n.grid-container {\r\n\tdisplay: flex;\r\n\tgap: 3px;\r\n\tmin-width: fit-content;\r\n}\r\n\r\n.day-labels {\r\n\tdisplay: flex;\r\n\tflex-direction: column;\r\n\twidth: 24px;\r\n\tgap: 2px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.day-label {\r\n\theight: 11px;\r\n\tfont-size: 9px;\r\n\tcolor: #7d8590;\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n}\r\n\r\n.contribution-grid {\r\n\tdisplay: flex;\r\n\tgap: 2px;\r\n\tflex: 1;\r\n\tmin-width: 0;\r\n}\r\n\r\n.contribution-week {\r\n\tdisplay: flex;\r\n\tflex-direction: column;\r\n\tgap: 2px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.contribution-day {\r\n\twidth: 11px;\r\n\theight: 11px;\r\n\tborder-radius: 2px;\r\n\tcursor: pointer;\r\n\toutline: 1px solid rgba(27, 31, 36, 0.06);\r\n\toutline-offset: -1px;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n/* Color schemes */\r\n.contribution-day.level-0.green {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.green {\r\n\tbackground-color: #0e4429;\r\n}\r\n.contribution-day.level-2.green {\r\n\tbackground-color: #006d32;\r\n}\r\n.contribution-day.level-3.green {\r\n\tbackground-color: #26a641;\r\n}\r\n.contribution-day.level-4.green {\r\n\tbackground-color: #39d353;\r\n}\r\n\r\n.contribution-day.level-0.blue {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.blue {\r\n\tbackground-color: #0a3069;\r\n}\r\n.contribution-day.level-2.blue {\r\n\tbackground-color: #1f6feb;\r\n}\r\n.contribution-day.level-3.blue {\r\n\tbackground-color: #58a6ff;\r\n}\r\n.contribution-day.level-4.blue {\r\n\tbackground-color: #79c0ff;\r\n}\r\n\r\n.contribution-day.level-0.purple {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.purple {\r\n\tbackground-color: #3b1e6d;\r\n}\r\n.contribution-day.level-2.purple {\r\n\tbackground-color: #8250df;\r\n}\r\n.contribution-day.level-3.purple {\r\n\tbackground-color: #a475f9;\r\n}\r\n.contribution-day.level-4.purple {\r\n\tbackground-color: #d2a8ff;\r\n}\r\n\r\n.contribution-day.level-0.orange {\r\n\tbackground-color: #161b22;\r\n}\r\n.contribution-day.level-1.orange {\r\n\tbackground-color: #7d2d00;\r\n}\r\n.contribution-day.level-2.orange {\r\n\tbackground-color: #da7b00;\r\n}\r\n.contribution-day.level-3.orange {\r\n\tbackground-color: #ffa348;\r\n}\r\n.contribution-day.level-4.orange {\r\n\tbackground-color: #ffb366;\r\n}\r\n\r\n.contribution-day:hover {\r\n\toutline: 1px solid #c9d1d9;\r\n\toutline-offset: -1px;\r\n}\r\n\r\n.graph-footer {\r\n\tdisplay: flex;\r\n\tjustify-content: space-between;\r\n\talign-items: center;\r\n\tmargin-top: 8px;\r\n}\r\n\r\n.last-updated {\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.legend {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tgap: 4px;\r\n}\r\n\r\n.legend-label {\r\n\tcolor: #7d8590;\r\n}\r\n\r\n.legend-squares {\r\n\tdisplay: flex;\r\n\tgap: 2px;\r\n}\r\n\r\n.legend-squares .contribution-day {\r\n\tcursor: default;\r\n}\r\n\r\n.legend-squares .contribution-day:hover {\r\n\toutline: none;\r\n}\r\n\r\n/* Mobile responsive */\r\n@media (max-width: 768px) {\r\n\t.git-contribution-graph {\r\n\t\tpadding: 12px;\r\n\t\tfont-size: 11px;\r\n\t\toverflow-x: auto;\r\n\t}\r\n\t.months-container {\r\n\t\tgrid-template-columns: repeat(53, 10px);\r\n\t\tgap: 1px;\r\n\t}\r\n\t.grid-container {\r\n\t\tgap: 2px;\r\n\t}\r\n\t.day-labels {\r\n\t\twidth: 20px;\r\n\t}\r\n\t.day-label {\r\n\t\theight: 10px;\r\n\t\tfont-size: 8px;\r\n\t}\r\n\t.contribution-grid {\r\n\t\tgap: 1px;\r\n\t}\r\n\t.contribution-week {\r\n\t\tgap: 1px;\r\n\t}\r\n\t.contribution-day {\r\n\t\twidth: 10px;\r\n\t\theight: 10px;\r\n\t}\r\n\t.settings-btn {\r\n\t\tfont-size: 10px;\r\n\t\tpadding: 4px 8px;\r\n\t}\r\n\t.contribution-count {\r\n\t\tfont-size: 14px;\r\n\t}\r\n}\r\n\r\n@media (max-width: 480px) {\r\n\t.graph-header {\r\n\t\tflex-direction: column;\r\n\t\talign-items: flex-start;\r\n\t\tgap: 8px;\r\n\t}\r\n}\r\n</style>\r\n","<template>\r\n\t<div class=\"git-stats-breakdown\">\r\n\t\t<div class=\"stats-grid\">\r\n\t\t\t<!-- Years Experience -->\r\n\t\t\t<div class=\"stat-card\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-experience\">⏱️</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div class=\"stat-value\">{{ yearsExperience }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">Years Experience</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Projects -->\r\n\t\t\t<div class=\"stat-card\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-projects\">📦</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div v-if=\"loading\" class=\"stat-loading\">\r\n\t\t\t\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div v-else class=\"stat-value\">{{ totalProjects }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">Projects</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Commits -->\r\n\t\t\t<div class=\"stat-card\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-commits\">💻</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div v-if=\"loading\" class=\"stat-loading\">\r\n\t\t\t\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div v-else class=\"stat-value\">{{ totalCommits }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">Commits</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t<!-- Custom Stat -->\r\n\t\t\t<div class=\"stat-card\" v-if=\"showCustomStat\">\r\n\t\t\t\t<div class=\"stat-icon\">\r\n\t\t\t\t\t<slot name=\"icon-custom\">☕</slot>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"stat-content\">\r\n\t\t\t\t\t<div v-if=\"loading\" class=\"stat-loading\">\r\n\t\t\t\t\t\t<div class=\"spinner\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div v-else class=\"stat-value\">{{ customStatValue }}</div>\r\n\t\t\t\t\t<div class=\"stat-label\">\r\n\t\t\t\t\t\t<slot name=\"custom-stat-label\">Coffee Consumed</slot>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\r\n\t\t<!-- Footer -->\r\n\t\t<div class=\"stats-footer\">\r\n\t\t\t<small v-if=\"dataSourceText\" class=\"data-source\">\r\n\t\t\t\t{{ dataSourceText }}\r\n\t\t\t\t<span v-if=\"lastUpdatedText\"> · {{ lastUpdatedText }}</span>\r\n\t\t\t</small>\r\n\t\t</div>\r\n\t</div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport { useGitStats } from '../composables/useGitStats'\r\nimport {\r\n\tcalculateYearsExperience,\r\n\ttype ExperienceEntry,\r\n\ttype CustomStatCalculator,\r\n\ttype CustomStatCalculatorParams,\r\n} from '@git-stats-components/core'\r\n\r\ninterface Props {\r\n\tdataUrl?: string\r\n\tprofileIndexes?: number[]\r\n\texperienceData?: ExperienceEntry[]\r\n\tshowCustomStat?: boolean\r\n\tcustomStatCalculator?: CustomStatCalculator | null\r\n\tcacheTTL?: number\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n\tdataUrl: '/data/git-stats.json',\r\n\tprofileIndexes: () => [],\r\n\texperienceData: () => [],\r\n\tshowCustomStat: true,\r\n\tcustomStatCalculator: null,\r\n\tcacheTTL: 24 * 60 * 60 * 1000,\r\n})\r\n\r\n// Use the shared composable\r\nconst { data, loading, dataSourceText, lastUpdatedText } = useGitStats({\r\n\tdataUrl: props.dataUrl,\r\n\tcacheTTL: props.cacheTTL,\r\n})\r\n\r\nconst totalProjects = ref(0)\r\nconst totalCommits = ref(0)\r\n\r\n// Calculate totals when data loads\r\nwatch(\r\n\tdata,\r\n\t(newData) => {\r\n\t\tif (!newData) return\r\n\r\n\t\t// If profileIndexes specified, sum only those profiles\r\n\t\tif (props.profileIndexes.length > 0) {\r\n\t\t\tlet projects = 0\r\n\t\t\tlet commits = 0\r\n\r\n\t\t\tprops.profileIndexes.forEach((index) => {\r\n\t\t\t\tconst profile = newData.profiles?.[index]\r\n\t\t\t\tif (profile?.stats) {\r\n\t\t\t\t\tprojects += profile.stats.projectCount || 0\r\n\t\t\t\t\tcommits += profile.stats.commitCount || 0\r\n\t\t\t\t}\r\n\t\t\t})\r\n\r\n\t\t\ttotalProjects.value = projects\r\n\t\t\ttotalCommits.value = commits\r\n\t\t} else {\r\n\t\t\t// Use totals from data (aggregates all profiles)\r\n\t\t\ttotalProjects.value = newData.totals?.projectCount || 0\r\n\t\t\ttotalCommits.value = newData.totals?.commitCount || 0\r\n\t\t}\r\n\t},\r\n\t{ immediate: true }\r\n)\r\n\r\n// Calculate years of experience using core utility\r\nconst yearsExperience = computed(() => {\r\n\tconst years = calculateYearsExperience(props.experienceData)\r\n\treturn years.toFixed(1)\r\n})\r\n\r\n// Custom stat calculation\r\nconst customStatValue = computed(() => {\r\n\tif (props.customStatCalculator) {\r\n\t\tconst params: CustomStatCalculatorParams = {\r\n\t\t\tprojects: totalProjects.value,\r\n\t\t\tcommits: totalCommits.value,\r\n\t\t\tyears: parseFloat(yearsExperience.value),\r\n\t\t}\r\n\t\treturn props.customStatCalculator(params)\r\n\t}\r\n\r\n\t// Default: fun coffee calculation\r\n\tconst kA = 1.5\r\n\tconst kB = 1.2\r\n\tconst kC = 1.5\r\n\r\n\tconst cups =\r\n\t\ttotalProjects.value * kA +\r\n\t\ttotalCommits.value * kB +\r\n\t\tparseFloat(yearsExperience.value) * kC\r\n\r\n\treturn cups.toFixed(2)\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.git-stats-breakdown {\r\n\tfont-family:\r\n\t\t-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,\r\n\t\tArial, sans-serif;\r\n\tpadding: 40px 20px;\r\n\tmax-width: 1200px;\r\n\tmargin: 0 auto;\r\n}\r\n\r\n.stats-grid {\r\n\tdisplay: grid;\r\n\tgrid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\r\n\tgap: 24px;\r\n\tmargin-bottom: 24px;\r\n}\r\n\r\n.stat-card {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tgap: 16px;\r\n\tpadding: 24px;\r\n\tbackground: rgba(255, 255, 255, 0.05);\r\n\tborder-radius: 12px;\r\n\tborder: 1px solid rgba(255, 255, 255, 0.1);\r\n\ttransition: all 0.3s ease;\r\n}\r\n\r\n.stat-card:hover {\r\n\tbackground: rgba(255, 255, 255, 0.08);\r\n\tborder-color: rgba(255, 255, 255, 0.2);\r\n\ttransform: translateY(-2px);\r\n}\r\n\r\n.stat-icon {\r\n\tfont-size: 48px;\r\n\tline-height: 1;\r\n\topacity: 0.9;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.stat-content {\r\n\tflex: 1;\r\n\tmin-width: 0;\r\n}\r\n\r\n.stat-value {\r\n\tfont-size: 32px;\r\n\tfont-weight: bold;\r\n\tline-height: 1.2;\r\n\tcolor: #e6edf3;\r\n\tmargin-bottom: 4px;\r\n}\r\n\r\n.stat-label {\r\n\tfont-size: 14px;\r\n\tcolor: #7d8590;\r\n\ttext-transform: uppercase;\r\n\tletter-spacing: 0.5px;\r\n}\r\n\r\n.stat-loading {\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tjustify-content: center;\r\n\theight: 38px;\r\n}\r\n\r\n.spinner {\r\n\twidth: 24px;\r\n\theight: 24px;\r\n\tborder: 3px solid rgba(255, 255, 255, 0.1);\r\n\tborder-top-color: #58a6ff;\r\n\tborder-radius: 50%;\r\n\tanimation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n\tto {\r\n\t\ttransform: rotate(360deg);\r\n\t}\r\n}\r\n\r\n.stats-footer {\r\n\tdisplay: flex;\r\n\tflex-direction: column;\r\n\talign-items: center;\r\n\tgap: 8px;\r\n\tpadding-top: 16px;\r\n\tborder-top: 1px solid rgba(255, 255, 255, 0.1);\r\n}\r\n\r\n.data-source {\r\n\tfont-size: 12px;\r\n\tcolor: #7d8590;\r\n\ttext-align: center;\r\n}\r\n\r\n/* Responsive */\r\n@media (max-width: 768px) {\r\n\t.git-stats-breakdown {\r\n\t\tpadding: 20px 12px;\r\n\t}\r\n\t.stats-grid {\r\n\t\tgrid-template-columns: 1fr;\r\n\t\tgap: 16px;\r\n\t}\r\n\t.stat-card {\r\n\t\tpadding: 16px;\r\n\t}\r\n\t.stat-icon {\r\n\t\tfont-size: 36px;\r\n\t}\r\n\t.stat-value {\r\n\t\tfont-size: 24px;\r\n\t}\r\n\t.stat-label {\r\n\t\tfont-size: 12px;\r\n\t}\r\n}\r\n\r\n@media (max-width: 480px) {\r\n\t.stat-card {\r\n\t\tflex-direction: column;\r\n\t\ttext-align: center;\r\n\t}\r\n\t.stat-content {\r\n\t\twidth: 100%;\r\n\t}\r\n}\r\n</style>\r\n","// Main entry point for git-stats-components\n\nimport type { App } from 'vue'\nimport ContributionGraph from './components/ContributionGraph.vue'\nimport StatsBreakdown from './components/StatsBreakdown.vue'\nimport { useGitStats } from './composables/useGitStats'\n\n// Re-export everything from core\nexport * from '@git-stats-components/core'\n\n// Export Vue-specific components and composables\nexport { ContributionGraph, StatsBreakdown, useGitStats }\n\n// Auto-import styles\nimport './styles/index.css'\n\n// Plugin for Vue.use()\nexport interface VueGitStatsPlugin {\n\tinstall: (app: App) => void\n}\n\nconst VueGitStats: VueGitStatsPlugin = {\n\tinstall(app: App) {\n\t\tapp.component('ContributionGraph', ContributionGraph)\n\t\tapp.component('StatsBreakdown', StatsBreakdown)\n\t},\n}\n\n// Export as default for Vue.use()\nexport default VueGitStats\n"],"names":["fetchGitStats","url","__async","response","generateDummyContributions","weeks","now","endDate","startDate","currentDate","week","weekData","day","isInFuture","isWeekend","dayCount","generateDummyStats","options","username","platform","projectCount","commitCount","contributions","total","sum","generateMultiProfileDummyStats","githubProfile","gitlabProfile","saveDummyDataToFile","filepath","data","json","blob","a","err","dataUrl","cacheKey","useStaleCache","__spreadProps","__spreadValues","_a","cached","_b","generateMockData","formatLastUpdated","dateString","date","diffHours","diffDays","getContributionLevel","count","calculateYearsExperience","experienceData","skillExperience","exp","end","start","years","skill","useGitStats","config","cacheTTL","loading","ref","error","dataSource","isDummy","loadData","result","lastUpdatedText","computed","dataSourceText","props","__props","emit","__emit","currentColorScheme","settingsOpen","colorSchemes","contributionData","monthLabels","watch","newData","_c","processContributions","generateMonthLabels","totalContributions","weekTotal","generateEmptyWeeks","generateEmptyWeek","i","days","monthPositions","lastMonth","lastYear","weekIndex","firstDay","dateParts","year","month","getContributionLevelNumber","getTooltipText","dayNum","formattedDate","contributionText","onDayClick","toggleSettings","changeColorScheme","scheme","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_normalizeClass","_unref","_hoisted_5","_renderSlot","_ctx","_hoisted_6","_Fragment","_renderList","$event","_hoisted_7","_hoisted_8","_cache","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_normalizeStyle","_hoisted_13","_hoisted_14","_hoisted_16","_hoisted_17","totalProjects","totalCommits","projects","commits","index","profile","yearsExperience","customStatValue","params","_hoisted_15","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_createTextVNode","_hoisted_25","VueGitStats","app","ContributionGraph","StatsBreakdown"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@git-stats-components/vue",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Beautiful GitHub/GitLab/Bitbucket contribution graphs for Vue 3",
|
|
5
|
+
"author": "Derek Johnston",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/vue.umd.js",
|
|
9
|
+
"module": "./dist/vue.es.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/vue.es.js",
|
|
15
|
+
"require": "./dist/vue.umd.js"
|
|
16
|
+
},
|
|
17
|
+
"./style.css": "./dist/style.css"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"vue",
|
|
24
|
+
"vue3",
|
|
25
|
+
"github",
|
|
26
|
+
"gitlab",
|
|
27
|
+
"bitbucket",
|
|
28
|
+
"contributions",
|
|
29
|
+
"stats",
|
|
30
|
+
"component"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "vite build",
|
|
34
|
+
"dev": "vite build --watch",
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"test:ui": "vitest --ui"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"vue": "^3.3.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@git-stats-components/core": "^1.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
48
|
+
"@types/node": "^20.10.6",
|
|
49
|
+
"@vue/test-utils": "^2.4.3",
|
|
50
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
51
|
+
"jsdom": "^23.0.1",
|
|
52
|
+
"typescript": "^5.3.3",
|
|
53
|
+
"vite": "^5.0.0",
|
|
54
|
+
"vite-plugin-dts": "^3.7.0",
|
|
55
|
+
"vitest": "^1.0.4",
|
|
56
|
+
"@vitest/ui": "^1.0.4",
|
|
57
|
+
"vue": "^3.4.0",
|
|
58
|
+
"vue-tsc": "^2.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|