@git-stats-components/vue 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -0
- package/dist/{vue-git-stats.es.js → vue.es.js} +1 -1
- package/dist/vue.es.js.map +1 -0
- package/dist/{vue-git-stats.umd.js → vue.umd.js} +1 -1
- package/dist/vue.umd.js.map +1 -0
- package/package.json +71 -59
- package/dist/vue-git-stats.es.js.map +0 -1
- package/dist/vue-git-stats.umd.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# @git-stats-components/vue
|
|
2
|
+
|
|
3
|
+
Beautiful GitHub/GitLab/Bitbucket contribution graphs for Vue 3.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @git-stats-components/vue
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```vue
|
|
14
|
+
<script setup>
|
|
15
|
+
import { ContributionGraph, StatsBreakdown } from '@git-stats-components/vue'
|
|
16
|
+
import '@git-stats-components/vue/style.css'
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div>
|
|
21
|
+
<ContributionGraph data-url="/data/git-stats.json" color-scheme="green" />
|
|
22
|
+
<StatsBreakdown data-url="/data/git-stats.json" />
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Components
|
|
28
|
+
|
|
29
|
+
### ContributionGraph
|
|
30
|
+
|
|
31
|
+
GitHub-style contribution heatmap.
|
|
32
|
+
|
|
33
|
+
**Props:**
|
|
34
|
+
|
|
35
|
+
- `dataUrl` (string) - Path to stats JSON file (default: `/data/git-stats.json`)
|
|
36
|
+
- `profileIndex` (number) - Which profile to display (default: 0)
|
|
37
|
+
- `colorScheme` ('green' | 'blue' | 'purple' | 'orange') - Color theme (default: 'green')
|
|
38
|
+
- `showSettings` (boolean) - Show color scheme dropdown (default: true)
|
|
39
|
+
- `cacheTTL` (number) - Cache duration in milliseconds
|
|
40
|
+
|
|
41
|
+
**Events:**
|
|
42
|
+
|
|
43
|
+
- `@day-click` - Emitted when a day is clicked (`{ date: string, count: number }`)
|
|
44
|
+
- `@color-scheme-change` - Emitted when color scheme changes
|
|
45
|
+
|
|
46
|
+
**Example:**
|
|
47
|
+
|
|
48
|
+
```vue
|
|
49
|
+
<ContributionGraph
|
|
50
|
+
data-url="/data/git-stats.json"
|
|
51
|
+
:profile-index="0"
|
|
52
|
+
color-scheme="blue"
|
|
53
|
+
:show-settings="true"
|
|
54
|
+
@day-click="handleDayClick"
|
|
55
|
+
@color-scheme-change="handleColorChange"
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### StatsBreakdown
|
|
60
|
+
|
|
61
|
+
Project and commit count statistics.
|
|
62
|
+
|
|
63
|
+
**Props:**
|
|
64
|
+
|
|
65
|
+
- `dataUrl` (string) - Path to stats JSON file
|
|
66
|
+
- `profileIndexes` (number[]) - Which profiles to aggregate (default: [])
|
|
67
|
+
- `experienceData` (ExperienceEntry[]) - Work experience for years calculation
|
|
68
|
+
- `showCustomStat` (boolean) - Show custom stat (default: true)
|
|
69
|
+
- `customStatCalculator` (function) - Custom stat calculation function
|
|
70
|
+
|
|
71
|
+
**Slots:**
|
|
72
|
+
|
|
73
|
+
- `icon-experience` - Custom icon for experience stat
|
|
74
|
+
- `icon-projects` - Custom icon for projects stat
|
|
75
|
+
- `icon-commits` - Custom icon for commits stat
|
|
76
|
+
- `icon-custom` - Custom icon for custom stat
|
|
77
|
+
- `custom-stat-label` - Custom label for custom stat
|
|
78
|
+
|
|
79
|
+
**Example:**
|
|
80
|
+
|
|
81
|
+
```vue
|
|
82
|
+
<script setup>
|
|
83
|
+
const experienceData = [
|
|
84
|
+
{
|
|
85
|
+
startDate: '2020-01-01',
|
|
86
|
+
endDate: null, // current
|
|
87
|
+
skills: ['JavaScript', 'Vue', 'TypeScript']
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
function calculatePizzas({ projects, commits, years }) {
|
|
92
|
+
return (projects * 2 + commits * 0.5 + years * 100).toFixed(0)
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<template>
|
|
97
|
+
<StatsBreakdown
|
|
98
|
+
data-url="/data/git-stats.json"
|
|
99
|
+
:experience-data="experienceData"
|
|
100
|
+
:show-custom-stat="true"
|
|
101
|
+
:custom-stat-calculator="calculatePizzas"
|
|
102
|
+
>
|
|
103
|
+
<template #icon-custom>🍕</template>
|
|
104
|
+
<template #custom-stat-label>Pizzas Ordered</template>
|
|
105
|
+
</StatsBreakdown>
|
|
106
|
+
</template>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Using the Composable
|
|
110
|
+
|
|
111
|
+
```vue
|
|
112
|
+
<script setup>
|
|
113
|
+
import { useGitStats } from '@git-stats-components/vue'
|
|
114
|
+
|
|
115
|
+
const { data, loading, error, dataSourceText, lastUpdatedText, isDummy } = useGitStats({
|
|
116
|
+
dataUrl: '/data/git-stats.json',
|
|
117
|
+
cacheTTL: 3600000, // 1 hour
|
|
118
|
+
useStaleCache: true
|
|
119
|
+
})
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<template>
|
|
123
|
+
<div v-if="loading">Loading...</div>
|
|
124
|
+
<div v-else-if="error">Error: {{ error.message }}</div>
|
|
125
|
+
<div v-else>
|
|
126
|
+
<p>{{ dataSourceText }}</p>
|
|
127
|
+
<p>{{ lastUpdatedText }}</p>
|
|
128
|
+
<pre>{{ JSON.stringify(data, null, 2) }}</pre>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## TypeScript Support
|
|
134
|
+
|
|
135
|
+
Full TypeScript support with exported types:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import type {
|
|
139
|
+
GitStatsData,
|
|
140
|
+
ColorScheme,
|
|
141
|
+
Platform,
|
|
142
|
+
ExperienceEntry,
|
|
143
|
+
CustomStatCalculator
|
|
144
|
+
} from '@git-stats-components/vue'
|
|
145
|
+
|
|
146
|
+
const colorScheme: ColorScheme = 'green'
|
|
147
|
+
|
|
148
|
+
const experienceData: ExperienceEntry[] = [
|
|
149
|
+
{
|
|
150
|
+
startDate: '2020-01-01',
|
|
151
|
+
endDate: null,
|
|
152
|
+
skills: ['JavaScript', 'Vue', 'TypeScript']
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
const customCalculator: CustomStatCalculator = ({ projects, commits, years }) => {
|
|
157
|
+
return (projects * 2 + commits * 0.5).toFixed(0)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Plugin Usage
|
|
162
|
+
|
|
163
|
+
Register globally in your Vue app:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { createApp } from 'vue'
|
|
167
|
+
import VueGitStats from '@git-stats-components/vue'
|
|
168
|
+
import '@git-stats-components/vue/style.css'
|
|
169
|
+
import App from './App.vue'
|
|
170
|
+
|
|
171
|
+
const app = createApp(App)
|
|
172
|
+
app.use(VueGitStats)
|
|
173
|
+
app.mount('#app')
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Then use without imports:
|
|
177
|
+
|
|
178
|
+
```vue
|
|
179
|
+
<template>
|
|
180
|
+
<ContributionGraph data-url="/data/git-stats.json" />
|
|
181
|
+
<StatsBreakdown data-url="/data/git-stats.json" />
|
|
182
|
+
</template>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Nuxt 3 Usage
|
|
186
|
+
|
|
187
|
+
```vue
|
|
188
|
+
<!-- pages/index.vue -->
|
|
189
|
+
<script setup lang="ts">
|
|
190
|
+
import { ContributionGraph, StatsBreakdown } from '@git-stats-components/vue'
|
|
191
|
+
import '@git-stats-components/vue/style.css'
|
|
192
|
+
|
|
193
|
+
const experienceData = [
|
|
194
|
+
{
|
|
195
|
+
startDate: '2020-01-01',
|
|
196
|
+
endDate: null,
|
|
197
|
+
skills: ['JavaScript', 'Vue', 'Nuxt']
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<template>
|
|
203
|
+
<div>
|
|
204
|
+
<ContributionGraph data-url="/data/git-stats.json" />
|
|
205
|
+
<StatsBreakdown data-url="/data/git-stats.json" :experience-data="experienceData" />
|
|
206
|
+
</div>
|
|
207
|
+
</template>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Custom Styling
|
|
211
|
+
|
|
212
|
+
Override CSS variables:
|
|
213
|
+
|
|
214
|
+
```css
|
|
215
|
+
.git-contribution-graph {
|
|
216
|
+
--graph-bg: #0d1117;
|
|
217
|
+
--graph-text: #e6edf3;
|
|
218
|
+
--graph-border: #30363d;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Or target specific classes */
|
|
222
|
+
.contribution-day.level-4.green {
|
|
223
|
+
background-color: #00ff00 !important;
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Data Setup
|
|
228
|
+
|
|
229
|
+
## Quick Setup
|
|
230
|
+
|
|
231
|
+
### 1. Initialize in your project
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npx @git-stats-components/vue init
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
This creates:
|
|
238
|
+
- `git-stats.config.js` - Configuration file
|
|
239
|
+
- `.github/workflows/update-git-stats.yml` - GitHub Action workflow
|
|
240
|
+
- `public/data/` - Directory for stats data
|
|
241
|
+
|
|
242
|
+
### 2. Configure your profiles
|
|
243
|
+
|
|
244
|
+
Edit `git-stats.config.js`:
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
export default {
|
|
248
|
+
profiles: [
|
|
249
|
+
{
|
|
250
|
+
username: 'your-github-username',
|
|
251
|
+
platform: 'github',
|
|
252
|
+
tokenSecret: 'GITHUB_TOKEN'
|
|
253
|
+
}
|
|
254
|
+
],
|
|
255
|
+
dataPath: 'public/data/git-stats.json',
|
|
256
|
+
schedule: '0 2 * * *' // Daily at 2 AM UTC
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 3. Add GitHub Secrets
|
|
261
|
+
|
|
262
|
+
Go to **Settings → Secrets and variables → Actions** and add your tokens.
|
|
263
|
+
|
|
264
|
+
### 4. Done!
|
|
265
|
+
|
|
266
|
+
The GitHub Action will fetch your stats daily and save them to the JSON file. Your components will load this data automatically.
|
|
267
|
+
|
|
268
|
+
For more details, see the main [git-stats-components](https://github.com/derekjj/git-stats-components) repository.
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT © Derek Johnston
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.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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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
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
|
|
2
|
+
//# sourceMappingURL=vue.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.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":"s5BAEA,SAAsBA,EAAcC,EAAoC,QAAAC,EAAA,sBACvE,MAAMC,EAAW,MAAM,MAAMF,CAAG,EAChC,GAAI,CAACE,EAAS,GACb,MAAM,IAAI,MAAM,8BAA8BA,EAAS,UAAU,EAAE,EAEpE,OAAOA,EAAS,KAAA,CACjB,GCDO,SAASC,GAA6B,CAC5C,MAAMC,EAAQ,CAAA,EACRC,EAAM,IAAI,KAGVC,EAAU,IAAI,KAAKD,CAAG,EAC5BC,EAAQ,QAAQA,EAAQ,QAAO,EAAKA,EAAQ,OAAM,CAAE,EAGpD,MAAMC,EAAY,IAAI,KAAKD,CAAO,EAClCC,EAAU,QAAQA,EAAU,QAAO,EAAK,GAAK,CAAC,EAE9C,MAAMC,EAAc,IAAI,KAAKD,CAAS,EAEtC,QAASE,EAAO,EAAGA,EAAO,GAAIA,IAAQ,CACrC,MAAMC,EAAW,CAChB,UAAW,IAAI,KAAKF,CAAW,EAAE,YAAW,EAAG,MAAM,GAAG,EAAE,CAAC,EAC3D,iBAAkB,CAAA,CACrB,EAEE,QAASG,EAAM,EAAGA,EAAM,EAAGA,IAAO,CACjC,MAAMC,EAAaJ,EAAcH,EAC3BQ,EAAYF,IAAQ,GAAKA,IAAQ,EAGvC,IAAIG,EAAW,EACVF,IACAC,EACHC,EACC,KAAK,SAAW,GAAM,KAAK,MAAM,KAAK,SAAW,CAAC,EAAI,EAEvDA,EAAW,KAAK,MAAM,KAAK,OAAM,EAAK,EAAE,GAI1CJ,EAAS,iBAAiB,KAAK,CAC9B,KAAM,IAAI,KAAKF,CAAW,EAAE,YAAW,EAAG,MAAM,GAAG,EAAE,CAAC,EACtD,kBAAmBM,EACnB,QAASH,CACb,CAAI,EACDH,EAAY,QAAQA,EAAY,QAAO,EAAK,CAAC,CAC9C,CAEAJ,EAAM,KAAKM,CAAQ,CACpB,CAEA,OAAON,CACR,CAKO,SAASW,EAAmBC,EAAU,GAAI,CAChD,KAAM,CACL,SAAAC,EAAW,YACX,SAAAC,EAAW,SACX,aAAAC,EAAe,GACf,YAAAC,EAAc,IAChB,EAAKJ,EAEEK,EAAgBlB,EAA0B,EACrB,OAAAkB,EAAc,OAAO,CAACC,EAAOb,IAEtDa,EACAb,EAAK,iBAAiB,OACrB,CAACc,EAAKZ,IAAQY,EAAMZ,EAAI,kBACxB,CACJ,EAEI,CAAC,EAEG,CACN,YAAa,IAAI,KAAI,EAAG,YAAW,EACnC,SAAU,CACT,CACC,SAAAM,EACA,SAAAC,EACA,MAAO,CACN,aAAAC,EACA,YAAAC,EACA,cAAeC,EAAc,IAAKZ,IAAU,CAC3C,SAAUA,EAAK,UACf,iBAAkBA,EAAK,gBAC7B,EAAO,CACP,CACA,CACA,EACE,OAAQ,CACP,aAAAU,EACA,YAAAC,CACH,EACE,SAAU,CACT,UAAW,KAAK,IAAG,EACnB,OAAQ,aACR,QAAS,EACZ,CACA,CACA,CAKO,SAASI,GAAiC,CAChD,MAAMC,EAAgBV,EAAmB,CACxC,SAAU,cACV,SAAU,SACV,aAAc,GACd,YAAa,IACf,CAAE,EAEKW,EAAgBX,EAAmB,CACxC,SAAU,cACV,SAAU,SACV,aAAc,EACd,YAAa,GACf,CAAE,EAED,MAAO,CACN,YAAa,IAAI,KAAI,EAAG,YAAW,EACnC,SAAU,CAACU,EAAc,SAAS,CAAC,EAAGC,EAAc,SAAS,CAAC,CAAC,EAC/D,OAAQ,CACP,aAAc,GACd,YAAa,IAChB,EACE,SAAU,CACT,UAAW,KAAK,IAAG,EACnB,OAAQ,aACR,QAAS,EACZ,CACA,CACA,CAKO,SAASC,EAAoBC,EAAW,uBAAwB,CACtE,MAAMC,EAAOd,EAAkB,EACzBe,EAAO,KAAK,UAAUD,EAAM,KAAM,GAAI,EAE5C,GAAI,OAAO,QAAW,YAAa,CAElC,MAAME,EAAO,IAAI,KAAK,CAACD,CAAI,EAAG,CAAE,KAAM,kBAAkB,CAAE,EACpD9B,EAAM,IAAI,gBAAgB+B,CAAI,EAC9BC,EAAI,SAAS,cAAc,GAAG,EACpCA,EAAE,KAAOhC,EACTgC,EAAE,SAAWJ,EACbI,EAAE,MAAK,EACP,IAAI,gBAAgBhC,CAAG,CACxB,KAEC,IAAI,CACQ,QAAQ,IAAI,EACpB,cAAc4B,EAAUE,CAAI,EAC/B,QAAQ,IAAI,yBAAyBF,CAAQ,EAAE,CAChD,OAASK,EAAK,CACb,QAAQ,MAAM,6BAA8BA,CAAG,CAChD,CAEF,CClHA,SAAsBlC,EACrBiB,EACoC,QAAAf,EAAA,8BACpC,KAAM,CAAE,QAAAiC,EAAS,SAAAC,EAAW,kBAAmB,cAAAC,EAAgB,IAASpB,EAExE,GAAI,CAEH,MAAMd,EAAW,MAAM,MAAMgC,CAAO,EACpC,GAAIhC,EAAS,GAAI,CAChB,MAAM2B,EAAO,MAAM3B,EAAS,KAAA,EAE5B,OAAI,OAAO,QAAW,aACrB,aAAa,QACZiC,EACA,KAAK,UAAUE,EAAAC,EAAA,GACXT,GADW,CAEd,SAAU,KAAK,IAAA,CAAI,EACnB,CAAA,EAGI,CACN,KAAAA,EACA,MAAO,KACP,OAAQ,SACR,UAASU,EAAAV,EAAK,WAAL,YAAAU,EAAe,WAAY,EAAA,CAEtC,CACD,OAASN,EAAK,CACb,QAAQ,KAAK,oCAAqCA,CAAG,CACtD,CAGA,GAAIG,GAAiB,OAAO,QAAW,YACtC,GAAI,CACH,MAAMI,EAAS,aAAa,QAAQL,CAAQ,EAC5C,GAAIK,EAAQ,CACX,MAAMX,EAAO,KAAK,MAAMW,CAAM,EAC9B,MAAO,CACN,KAAAX,EACA,MAAO,KACP,OAAQ,QACR,UAASY,EAAAZ,EAAK,WAAL,YAAAY,EAAe,WAAY,EAAA,CAEtC,CACD,OAASR,EAAK,CACb,QAAQ,KAAK,6BAA8BA,CAAG,CAC/C,CAKD,MAAO,CACN,KAFgBS,EAAA,EAGhB,MAAO,KACP,OAAQ,OACR,QAAS,EAAA,CAEX,GAKO,SAASC,EAAkBC,EAA4B,CAC7D,MAAMC,EAAO,IAAI,KAAKD,CAAU,EAC1BvC,MAAU,KACVyC,EAAY,KAAK,OAAOzC,EAAI,QAAA,EAAYwC,EAAK,QAAA,IAAc,IAAO,GAAK,GAAG,EAEhF,GAAIC,EAAY,EAAG,MAAO,WAC1B,GAAIA,EAAY,GAAI,MAAO,GAAGA,CAAS,aAEvC,MAAMC,EAAW,KAAK,MAAMD,EAAY,EAAE,EAC1C,OAAIC,IAAa,EAAU,YACvBA,EAAW,EAAU,GAAGA,CAAQ,YAE7BF,EAAK,mBAAmB,QAAS,CACvC,MAAO,QACP,IAAK,UACL,KAAMA,EAAK,YAAA,IAAkBxC,EAAI,YAAA,EAAgB,UAAY,MAAA,CAC7D,CACF,CAKO,SAAS2C,EAAqBC,EAAuB,CAC3D,OAAIA,IAAU,EAAU,EACpBA,GAAS,EAAU,EACnBA,GAAS,EAAU,EACnBA,GAAS,EAAU,EAChB,CACR,CAKO,SAASC,EACfC,EACS,CACT,GAAIA,EAAe,SAAW,EAAG,MAAO,GAExC,MAAMC,EAA0C,CAAA,EAEhD,OAAAD,EAAe,QAASE,GAAQ,OAC/B,MAAMC,EAAMD,EAAI,QAAU,IAAI,KAAKA,EAAI,OAAO,EAAI,IAAI,KAChDE,EAAQ,IAAI,KAAKF,EAAI,SAAS,EAC9BG,GAASF,EAAI,QAAA,EAAYC,EAAM,YAAc,IAAO,GAAK,GAAK,GAAK,SAEzEhB,EAAAc,EAAI,SAAJ,MAAAd,EAAY,QAASkB,GAAU,CACzBL,EAAgBK,CAAK,IACzBL,EAAgBK,CAAK,EAAI,GAE1BL,EAAgBK,CAAK,GAAKD,CAC3B,EACD,CAAC,EAEM,KAAK,IAAI,GAAG,OAAO,OAAOJ,CAAe,EAAG,CAAC,CACrD,CAGA,SAASV,GAAiC,CACzC,MAAO,CACN,YAAa,IAAI,KAAA,EAAO,YAAA,EACxB,SAAU,CACT,CACC,SAAU,WACV,SAAU,SACV,MAAO,CACN,aAAc,GACd,YAAa,KACb,cAAe,CAAA,CAAC,CACjB,CACD,EAED,OAAQ,CACP,aAAc,GACd,YAAa,IAAA,EAEd,SAAU,CACT,OAAQ,OACR,UAAW,KAAK,IAAA,CAAI,CACrB,CAEF,CClLO,SAASgB,EAAYC,EAA4B,GAAI,CAC3D,KAAM,CACL,QAAAzB,EAAU,uBACV,SAAA0B,EAAW,GAAK,GAAK,GAAK,IAC1B,cAAAxB,EAAgB,GAChB,SAAAD,EAAW,iBAAA,EACRwB,EAEEE,EAAUC,EAAAA,IAAI,EAAK,EACnBC,EAAQD,EAAAA,IAAkB,IAAI,EAC9BjC,EAAOiC,EAAAA,IAAyB,IAAI,EACpCE,EAAaF,EAAAA,IAAuB,IAAI,EACxCG,EAAUH,EAAAA,IAAI,EAAK,EAKzB,SAAeI,GAAyC,QAAAjE,EAAA,sBACvD4D,EAAQ,MAAQ,GAChBE,EAAM,MAAQ,KAEd,GAAI,CACH,MAAMI,EAAS,MAAMpE,EAAc,CAClC,QAAAmC,EACA,SAAA0B,EACA,SAAAzB,EACA,cAAAC,CAAA,CACA,EAED,OAAAP,EAAK,MAAQsC,EAAO,KACpBJ,EAAM,MAAQI,EAAO,MACrBH,EAAW,MAAQG,EAAO,OAC1BF,EAAQ,MAAQE,EAAO,QAEhBA,EAAO,IACf,OAASlC,EAAK,CACb,OAAA8B,EAAM,MACL9B,aAAe,MAAQA,EAAM,IAAI,MAAM,qBAAqB,EACtD,IACR,QAAA,CACC4B,EAAQ,MAAQ,EACjB,CACD,GAKA,MAAMO,EAAkBC,EAAAA,SAAS,IAAM,OACtC,OAAK9B,EAAAV,EAAK,QAAL,MAAAU,EAAY,YACVI,EAAkBd,EAAK,MAAM,WAAW,EADV,EAEtC,CAAC,EAKKyC,EAAiBD,EAAAA,SAAS,IAAM,CACrC,GAAIJ,EAAQ,MACX,MAAO,kCAGR,OAAQD,EAAW,MAAA,CAClB,IAAK,SACJ,MAAO,iBACR,IAAK,QACJ,MAAO,cACR,IAAK,OACJ,MAAO,cACR,QACC,MAAO,EAAA,CAEV,CAAC,EAGD,OAAAE,EAAA,EAEO,CACN,KAAArC,EACA,QAAAgC,EACA,MAAAE,EACA,WAAAC,EACA,eAAAM,EACA,gBAAAF,EACA,QAAAH,EACA,SAAAC,CAAA,CAEF,mxBCyDA,MAAMK,EAAQC,EAQRC,EAAOC,EAGP,CAAE,KAAA7C,EAAM,QAAAgC,EAAS,eAAAS,EAAgB,gBAAAF,EAAiB,QAAAH,GAAYP,EACnE,CACC,QAASa,EAAM,QACf,SAAUA,EAAM,QAAA,CACjB,EAGKI,EAAqBb,EAAAA,IAAiBS,EAAM,WAAW,EACvDK,EAAed,EAAAA,IAAI,EAAK,EACxBe,EAA8B,CAAC,QAAS,OAAQ,SAAU,QAAQ,EAClEC,EAAmBhB,EAAAA,IAAqB,EAAE,EAC1CiB,EAAcjB,EAAAA,IAAkB,EAAE,EAGxCkB,EAAAA,MACCnD,EACCoD,GAAY,WACZ,IAAIC,GAAAzC,GAAAF,EAAA0C,GAAA,YAAAA,EAAS,WAAT,YAAA1C,EAAoBgC,EAAM,gBAA1B,YAAA9B,EAAyC,QAAzC,MAAAyC,EAAgD,cAAe,CAClE,MAAM7D,EACL4D,EAAQ,SAASV,EAAM,YAAY,EAAE,MAAM,cACxClD,IACHyD,EAAiB,MAAQK,EAAqB9D,CAAa,EAC3D+D,GAAA,EAEF,MACCN,EAAiB,MAAQ,CAAA,CAE3B,EACA,CAAE,UAAW,EAAA,CAAK,EAGnB,MAAMO,EAAqBhB,EAAAA,SAAS,IAC/B,CAACS,EAAiB,OAASA,EAAiB,MAAM,SAAW,EACzD,EAGDA,EAAiB,MAAM,OAAO,CAACxD,EAAOb,IACxC,CAACA,EAAK,MAAQ,CAAC,MAAM,QAAQA,EAAK,IAAI,EAClCa,EAGPA,EACAb,EAAK,KAAK,OAAO,CAAC6E,EAAW3E,IACrB2E,GAAa3E,EAAI,OAAS,GAC/B,CAAC,EAEH,CAAC,CACJ,EAED,SAASwE,EACR9D,EACkB,CAClB,GAAI,CAACA,GAAiB,CAAC,MAAM,QAAQA,CAAa,EACjD,OAAOkE,EAAA,EAGR,MAAMnF,EAAQiB,EAAc,IAAKZ,IAAU,CAC1C,UAAWA,EAAK,UAAY,GAC5B,KAAMA,EAAK,iBAAiB,IAAKE,GAAA,OAAS,OACzC,KAAMA,EAAI,MAAQ,GAClB,OAAO4B,EAAA5B,EAAI,oBAAJ,KAAA4B,EAAyB,EAChC,QAAS5B,EAAI,SAAW,CAAA,EACvB,CAAA,EACD,EAEF,KAAOP,EAAM,OAAS,IACrBA,EAAM,KAAKoF,GAAmB,EAG/B,OAAOpF,CACR,CAEA,SAASmF,GAAsC,CAC9C,MAAMnF,EAAyB,CAAA,EAC/B,QAASqF,EAAI,EAAGA,EAAI,GAAIA,IACvBrF,EAAM,KAAKoF,GAAmB,EAE/B,OAAOpF,CACR,CAEA,SAASoF,GAAmC,CAC3C,MAAME,EAAuB,CAAA,EAC7B,QAASD,EAAI,EAAGA,EAAI,EAAGA,IACtBC,EAAK,KAAK,CAAE,KAAM,GAAI,MAAO,EAAG,QAASD,EAAG,EAE7C,MAAO,CAAE,UAAW,GAAI,KAAAC,CAAA,CACzB,CAEA,SAASN,IAA4B,CACpC,GAAI,CAACN,EAAiB,OAASA,EAAiB,MAAM,SAAW,EAAG,CACnEC,EAAY,MAAQ,CAAA,EACpB,MACD,CAEA,MAAMY,EAA+B,CAAA,EACrC,IAAIC,EAAY,GACZC,EAAW,GAEff,EAAiB,MAAM,QAAQ,CAACrE,EAAMqF,IAAc,CACnD,GAAI,CAACrF,EAAK,MAAQA,EAAK,KAAK,SAAW,EAAG,OAE1C,MAAMsF,EAAWtF,EAAK,KAAK,CAAC,EAAE,KAC9B,GAAI,CAACsF,EAAU,OAEf,MAAMC,EAAYD,EAAS,MAAM,GAAG,EACpC,GAAIC,EAAU,SAAW,EAAG,OAE5B,KAAM,CAACC,EAAMC,CAAK,EAAIF,EAAU,IAAI,MAAM,EAC1C,GAAI,QAAMC,CAAI,GAAK,MAAMC,CAAK,KAE1BA,IAAUN,GAAaK,IAASJ,GAAU,CAC7C,MAAMhD,GAAO,IAAI,KAAKoD,EAAMC,EAAQ,EAAG,CAAC,EACxCP,EAAe,KAAK,CACnB,KAAMG,EACN,MAAOI,EAAQ,EACf,KAAAD,EACA,MAAOpD,GAAK,mBAAmB,QAAS,CAAE,MAAO,QAAS,CAAA,CAC1D,EACD+C,EAAYM,EACZL,EAAWI,CACZ,CACD,CAAC,EAEDlB,EAAY,MAAQY,CACrB,CAEA,SAAS3C,GAAqBC,EAAuB,CAEpD,MAAO,SADOkD,GAA2BlD,CAAK,CACzB,IAAI0B,EAAmB,KAAK,EAClD,CAEA,SAASwB,GAA2BlD,EAAuB,CAC1D,OAAIA,IAAU,EAAU,EACpBA,GAAS,EAAU,EACnBA,GAAS,EAAU,EACnBA,GAAS,EAAU,EAChB,CACR,CAEA,SAASmD,GAAezF,EAA2B,CAClD,GAAI,CAACA,EAAI,KAAM,MAAO,GAEtB,KAAM,CAACsF,EAAMC,EAAOG,CAAM,EAAI1F,EAAI,KAAK,MAAM,GAAG,EAAE,IAAI,MAAM,EAGtD2F,EAFO,IAAI,KAAKL,EAAMC,EAAQ,EAAGG,CAAM,EAElB,mBAAmB,QAAS,CACtD,QAAS,QACT,KAAM,UACN,MAAO,QACP,IAAK,SAAA,CACL,EAEKE,EAAmB5F,EAAI,QAAU,EAAI,eAAiB,gBAC5D,MAAO,GAAGA,EAAI,KAAK,IAAI4F,CAAgB,OAAOD,CAAa,EAC5D,CAEA,SAASE,GAAW7F,EAAyB,CAC5C8D,EAAK,YAAa,CAAE,KAAM9D,EAAI,KAAM,MAAOA,EAAI,MAAO,CACvD,CAEA,SAAS8F,IAAuB,CAC/B7B,EAAa,MAAQ,CAACA,EAAa,KACpC,CAEA,SAAS8B,GAAkBC,EAA2B,CACrDhC,EAAmB,MAAQgC,EAC3B/B,EAAa,MAAQ,GACrBH,EAAK,sBAAuBkC,CAAM,CACnC,eA/UCC,YAAA,EAAAC,qBAkHM,MAlHNC,EAkHM,CAhHLC,EAAAA,mBAiCM,MAjCNC,EAiCM,CAhCLD,EAAAA,mBAWM,MAXNE,EAWM,CAVLF,qBAGK,KAHLG,EAGKC,kBAFD9B,QAAmB,eAAA,GAAmB,mCAE1C,CAAA,EACA0B,EAAAA,mBAKQ,QAAA,CAJP,MAAKK,EAAAA,eAAA,CAAC,mBAAkB,CAAA,WACFC,EAAAA,MAAApD,CAAA,EAAO,CAAA,CAAA,oBAE1BoD,EAAAA,MAAA/C,CAAA,CAAc,EAAA,CAAA,CAAA,GAGeE,EAAA,cAAlCoC,EAAAA,UAAA,EAAAC,EAAAA,mBAmBM,MAnBNS,EAmBM,CAlBLP,EAAAA,mBAOS,SAAA,CANR,MAAM,eACN,KAAK,SACJ,QAAON,EAAA,GAERc,EAAAA,WAAoCC,4BAApC,IAAoC,+BAAT,KAAE,EAAA,EAAA,oCAAO,aAErC,EAAA,EAAA,GACW5C,EAAA,OAAXgC,EAAAA,UAAA,EAAAC,EAAAA,mBASM,MATNY,EASM,gBARLZ,EAAAA,mBAOSa,EAAAA,SAAA,KAAAC,aANS9C,EAAV8B,GADRI,EAAAA,mBAOS,SAAA,CALP,IAAKJ,EACL,QAAKiB,GAAElB,GAAkBC,CAAM,EAChC,MAAM,eAAA,EAEHQ,EAAAA,gBAAAR,CAAM,EAAG,UACb,EAAAkB,EAAA,wEAMQR,EAAAA,MAAAxD,CAAA,GAAX+C,YAAA,EAAAC,EAAAA,mBAGM,MAHNiB,GAGM,CAAA,GAAAC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CAFLhB,EAAAA,mBAA2B,MAAA,CAAtB,MAAM,SAAA,EAAS,KAAA,EAAA,EACpBA,EAAAA,mBAAqC,YAA/B,2BAAwB,EAAA,CAAA,OAI/BH,EAAAA,YAAAC,EAAAA,mBAqEM,MArENmB,GAqEM,CApELjB,EAAAA,mBAiDM,MAjDNkB,GAiDM,CA/CLlB,EAAAA,mBAcM,MAdNmB,GAcM,aAbLnB,EAAAA,mBAAgC,MAAA,CAA3B,MAAM,cAAA,EAAc,KAAA,EAAA,GACzBA,EAAAA,mBAWM,MAXNoB,GAWM,kBAVLtB,EAAAA,mBASMa,EAAAA,SAAA,KAAAC,EAAAA,WARW5C,EAAA,MAATmB,kBADRW,EAAAA,mBASM,MAAA,CAPJ,OAAQX,EAAM,IAAI,IAAIA,EAAM,KAAK,GAClC,MAAM,cACL,MAAKkC,EAAAA,eAAA,CAA4B,WAAA,GAAAlC,EAAM,KAAI,CAAA,WAAA,IAIzCiB,kBAAAjB,EAAM,KAAK,EAAA,CAAA,cAMjBa,EAAAA,mBA6BM,MA7BNsB,GA6BM,0ZAhBLtB,EAAAA,mBAeM,MAfNuB,GAeM,kBAdLzB,EAAAA,mBAaMa,EAAAA,SAAA,KAAAC,EAAAA,WAZU7C,EAAA,MAARrE,kBADRoG,EAAAA,mBAaM,MAAA,CAXJ,IAAKpG,EAAK,UACX,MAAM,mBAAA,IAENmG,YAAA,EAAA,EAAAC,EAAAA,mBAOOa,EAAAA,SAAA,KAAAC,EAAAA,WANQlH,EAAK,KAAZE,kBADRkG,EAAAA,mBAOO,MAAA,CALL,IAAKlG,EAAI,KACV,wBAAM,mBACEqC,GAAqBrC,EAAI,KAAK,CAAA,CAAA,EACrC,MAAOyF,GAAezF,CAAG,EACzB,QAAKiH,GAAEpB,GAAW7F,CAAG,CAAA,sCAQ3BoG,EAAAA,mBAeM,MAfNwB,GAeM,CAd6BlB,EAAAA,MAAAjD,CAAA,GAAlCwC,EAAAA,YAAAC,EAAAA,mBAEQ,QAFR2B,GAAmD,oCACjCnB,EAAAA,MAAAjD,CAAA,CAAe,EAAA,CAAA,+mDCZrC,MAAMG,EAAQC,EAUR,CAAE,KAAA3C,EAAM,QAAAgC,EAAS,eAAAS,EAAgB,gBAAAF,CAAA,EAAoBV,EAAY,CACtE,QAASa,EAAM,QACf,SAAUA,EAAM,QAAA,CAChB,EAEKkE,EAAgB3E,EAAAA,IAAI,CAAC,EACrB4E,EAAe5E,EAAAA,IAAI,CAAC,EAG1BkB,EAAAA,MACCnD,EACCoD,GAAY,SACZ,GAAKA,EAGL,GAAIV,EAAM,eAAe,OAAS,EAAG,CACpC,IAAIoE,EAAW,EACXC,EAAU,EAEdrE,EAAM,eAAe,QAASsE,GAAU,OACvC,MAAMC,GAAUvG,EAAA0C,EAAQ,WAAR,YAAA1C,EAAmBsG,GAC/BC,GAAA,MAAAA,EAAS,QACZH,GAAYG,EAAQ,MAAM,cAAgB,EAC1CF,GAAWE,EAAQ,MAAM,aAAe,EAE1C,CAAC,EAEDL,EAAc,MAAQE,EACtBD,EAAa,MAAQE,CACtB,MAECH,EAAc,QAAQlG,EAAA0C,EAAQ,SAAR,YAAA1C,EAAgB,eAAgB,EACtDmG,EAAa,QAAQjG,EAAAwC,EAAQ,SAAR,YAAAxC,EAAgB,cAAe,CAEtD,EACA,CAAE,UAAW,EAAA,CAAK,EAInB,MAAMsG,EAAkB1E,EAAAA,SAAS,IAClBnB,EAAyBqB,EAAM,cAAc,EAC9C,QAAQ,CAAC,CACtB,EAGKyE,EAAkB3E,EAAAA,SAAS,IAAM,CACtC,GAAIE,EAAM,qBAAsB,CAC/B,MAAM0E,EAAqC,CAC1C,SAAUR,EAAc,MACxB,QAASC,EAAa,MACtB,MAAO,WAAWK,EAAgB,KAAK,CAAA,EAExC,OAAOxE,EAAM,qBAAqB0E,CAAM,CACzC,CAYA,OAJCR,EAAc,MALJ,IAMVC,EAAa,MALH,IAMV,WAAWK,EAAgB,KAAK,EALtB,KAOC,QAAQ,CAAC,CACtB,CAAC,gBAnKAnC,YAAA,EAAAC,qBAiEM,MAjENC,GAiEM,CAhELC,EAAAA,mBAuDM,MAvDNC,GAuDM,CArDLD,EAAAA,mBAQM,MARNE,GAQM,CAPLF,EAAAA,mBAEM,MAFNG,GAEM,CADLK,EAAAA,WAAsCC,8BAAtC,IAAsC,+BAAT,KAAE,EAAA,EAAA,QAEhCT,EAAAA,mBAGM,MAHNO,GAGM,CAFLP,EAAAA,mBAAmD,MAAnDU,GAAmDN,EAAAA,gBAAxB4B,EAAA,KAAe,EAAA,CAAA,EAC1ChB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAhB,EAAAA,mBAA8C,MAAA,CAAzC,MAAM,cAAa,mBAAgB,EAAA,EAAA,KAK1CA,EAAAA,mBAWM,MAXNc,GAWM,CAVLd,EAAAA,mBAEM,MAFNe,GAEM,CADLP,EAAAA,WAAoCC,4BAApC,IAAoC,+BAAT,KAAE,EAAA,EAAA,QAE9BT,EAAAA,mBAMM,MANNiB,GAMM,CALMX,EAAAA,MAAAxD,CAAA,GAAX+C,YAAA,EAAAC,EAAAA,mBAEM,MAFNoB,GAEM,CAAA,GAAAF,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADLhB,EAAAA,mBAA2B,MAAA,CAAtB,MAAM,SAAA,EAAS,KAAA,EAAA,CAAA,qBAErBF,EAAAA,mBAAwD,MAAxDqB,GAAwDf,EAAAA,gBAAtBsB,EAAA,KAAa,EAAA,CAAA,GAC/CV,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAhB,EAAAA,mBAAsC,MAAA,CAAjC,MAAM,cAAa,WAAQ,EAAA,EAAA,KAKlCA,EAAAA,mBAWM,MAXNoB,GAWM,CAVLpB,EAAAA,mBAEM,MAFNsB,GAEM,CADLd,EAAAA,WAAmCC,2BAAnC,IAAmC,+BAAT,KAAE,EAAA,EAAA,QAE7BT,EAAAA,mBAMM,MANNuB,GAMM,CALMjB,EAAAA,MAAAxD,CAAA,GAAX+C,YAAA,EAAAC,EAAAA,mBAEM,MAFNqC,GAEM,CAAA,GAAAnB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADLhB,EAAAA,mBAA2B,MAAA,CAAtB,MAAM,SAAA,EAAS,KAAA,EAAA,CAAA,qBAErBF,EAAAA,mBAAuD,MAAvD0B,GAAuDpB,EAAAA,gBAArBuB,EAAA,KAAY,EAAA,CAAA,GAC9CX,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAhB,EAAAA,mBAAqC,MAAA,CAAhC,MAAM,cAAa,UAAO,EAAA,EAAA,KAKJvC,EAAA,gBAA7BoC,EAAAA,UAAA,EAAAC,EAAAA,mBAaM,MAbN2B,GAaM,CAZLzB,EAAAA,mBAEM,MAFNoC,GAEM,CADL5B,EAAAA,WAAiCC,0BAAjC,IAAiC,+BAAR,IAAC,EAAA,EAAA,QAE3BT,EAAAA,mBAQM,MARNqC,GAQM,CAPM/B,EAAAA,MAAAxD,CAAA,GAAX+C,YAAA,EAAAC,EAAAA,mBAEM,MAFNwC,GAEM,CAAA,GAAAtB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADLhB,EAAAA,mBAA2B,MAAA,CAAtB,MAAM,SAAA,EAAS,KAAA,EAAA,CAAA,qBAErBF,EAAAA,mBAA0D,MAA1DyC,GAA0DnC,EAAAA,gBAAxB6B,EAAA,KAAe,EAAA,CAAA,GACjDjC,EAAAA,mBAEM,MAFNwC,GAEM,CADLhC,EAAAA,WAAqDC,gCAArD,IAAqD,iCAAtB,kBAAe,EAAA,EAAA,2CAOlDT,EAAAA,mBAKM,MALNyC,GAKM,CAJQnC,EAAAA,MAAA/C,CAAA,GAAbsC,EAAAA,YAAAC,EAAAA,mBAGQ,QAHR4C,GAGQ,CAFJC,EAAAA,gBAAAvC,EAAAA,gBAAAE,EAAAA,MAAA/C,CAAA,CAAc,EAAG,IACpB,CAAA,EAAY+C,EAAAA,MAAAjD,CAAA,iBAAZyC,EAAAA,mBAA4D,OAAA8C,GAA/B,MAAGxC,EAAAA,gBAAGE,EAAAA,MAAAjD,CAAA,CAAe,EAAA,CAAA,gHC1ChDwF,GAAiC,CACtC,QAAQC,EAAU,CACjBA,EAAI,UAAU,oBAAqBC,CAAiB,EACpDD,EAAI,UAAU,iBAAkBE,CAAc,CAC/C,CACD"}
|
package/package.json
CHANGED
|
@@ -1,60 +1,72 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
2
|
+
"name": "@git-stats-components/vue",
|
|
3
|
+
"version": "1.0.2",
|
|
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
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"vue",
|
|
26
|
+
"vue3",
|
|
27
|
+
"github",
|
|
28
|
+
"gitlab",
|
|
29
|
+
"bitbucket",
|
|
30
|
+
"contributions",
|
|
31
|
+
"stats",
|
|
32
|
+
"component",
|
|
33
|
+
"git-stats"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/derekjj/git-stats-components.git",
|
|
38
|
+
"directory": "packages/vue"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/derekjj/git-stats-components/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/derekjj/git-stats-components/tree/main/packages/vue#readme",
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"vue": "^3.3.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@git-stats-components/core": "^1.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
52
|
+
"@types/node": "^20.10.6",
|
|
53
|
+
"@vue/test-utils": "^2.4.3",
|
|
54
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
55
|
+
"jsdom": "^23.0.1",
|
|
56
|
+
"typescript": "^5.3.3",
|
|
57
|
+
"vite": "^5.0.0",
|
|
58
|
+
"vite-plugin-dts": "^3.7.0",
|
|
59
|
+
"vitest": "^1.0.4",
|
|
60
|
+
"@vitest/ui": "^1.0.4",
|
|
61
|
+
"vue": "^3.4.0",
|
|
62
|
+
"vue-tsc": "^2.0.0"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "vite build",
|
|
66
|
+
"dev": "vite build --watch",
|
|
67
|
+
"clean": "rm -rf dist",
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"test:watch": "vitest",
|
|
70
|
+
"test:ui": "vitest --ui"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|