Compare commits

..

190 Commits

Author SHA1 Message Date
Ares-Bot
ba6ac7052a chore: sync profile metrics [skip ci] 2026-04-06 05:50:04 +00:00
gitea-analytics-bot
5f0a420ea5 Update analytics (2026-04-06T05:50:01Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-06 01:50:01 -04:00
Ares-Bot
2d454188cc chore: sync profile metrics [skip ci] 2026-04-05 05:50:04 +00:00
gitea-analytics-bot
c437e195e9 Update analytics (2026-04-05T05:50:02Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-05 01:50:02 -04:00
Ares-Bot
ac8596f8b3 chore: sync profile metrics [skip ci] 2026-04-04 22:36:34 +00:00
gitea-analytics-bot
23ad1e6f0b Update analytics (2026-04-04T22:36:31Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-04 18:36:31 -04:00
86007209e8 Merge branch 'main' of git.beane.me:patrick/.profile 2026-04-04 11:17:28 -04:00
f05fb7540a fix(profile): correct heatmap month labels 2026-04-04 11:17:24 -04:00
Ares-Bot
bdade0a4e8 chore: sync profile metrics [skip ci] 2026-04-04 15:11:16 +00:00
gitea-analytics-bot
57a0950bf2 Update analytics (2026-04-04T15:11:12Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-04 11:11:12 -04:00
00c390bb2c Merge branch 'main' of git.beane.me:patrick/.profile 2026-04-04 11:07:34 -04:00
5c4eb0daaa feat(profile): embed commit heatmap 2026-04-04 11:07:22 -04:00
Ares-Bot
7087f3e526 chore: sync code velocity to 47495 LOC [skip ci] 2026-04-04 14:35:24 +00:00
f37718e61c fix(actions): correct fields for commit history 2026-04-04 10:35:02 -04:00
220a433617 update(actions): use heatmap.json for authoritative commit history 2026-04-04 10:32:00 -04:00
Ares-Bot
5f9e58407e chore: sync code velocity to 47495 LOC [skip ci] 2026-04-04 14:22:52 +00:00
gitea-analytics-bot
2c956793c7 Update analytics (2026-04-04T14:22:48Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-04 10:22:48 -04:00
Ares-Bot
29beedd2f0 chore: sync code velocity to 47493 LOC [skip ci] 2026-04-04 14:11:04 +00:00
gitea-analytics-bot
2a49e2e60c Update analytics (2026-04-04T14:11:01Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-04 10:11:01 -04:00
Ares-Bot
903af90459 chore: sync code velocity to 47281 LOC [skip ci] 2026-04-04 05:50:04 +00:00
gitea-analytics-bot
373275cd7a Update code stats (2026-04-04T05:50:01.559205Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-04-04 01:50:01 -04:00
Ares-Bot
e7584f2698 chore: sync code velocity to 45879 LOC [skip ci] 2026-04-03 05:50:04 +00:00
gitea-analytics-bot
53ddc9fdbb Update code stats (2026-04-03T05:50:02.411232Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-03 01:50:02 -04:00
Ares-Bot
b527ea8b72 chore: sync code velocity to 42662 LOC [skip ci] 2026-04-02 05:50:04 +00:00
gitea-analytics-bot
259a16b752 Update code stats (2026-04-02T05:50:02.416269Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-02 01:50:02 -04:00
Ares-Bot
0b369d29df chore: sync code velocity to 42558 LOC [skip ci] 2026-04-01 05:50:02 +00:00
gitea-analytics-bot
1e7cff47ad Update code stats (2026-04-01T05:50:01.432082Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-04-01 01:50:01 -04:00
Ares-Bot
7265e29035 chore: sync code velocity to 40875 LOC [skip ci] 2026-03-31 15:25:48 +00:00
gitea-analytics-bot
23039c8b76 Update code stats (2026-03-31T15:25:45.570620Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-31 11:25:45 -04:00
Ares-Bot
1a6b5b0840 chore: sync code velocity to 34937 LOC [skip ci] 2026-03-31 05:50:04 +00:00
gitea-analytics-bot
0faa2e14bc Update code stats (2026-03-31T05:50:02.216389Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-31 01:50:02 -04:00
Ares-Bot
72c5a28e1f chore: sync code velocity to 34937 LOC [skip ci] 2026-03-30 05:50:04 +00:00
gitea-analytics-bot
d4a66c6530 Update code stats (2026-03-30T05:50:01.913983Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-30 01:50:01 -04:00
Ares-Bot
dcbdf1026d chore: sync code velocity to 33870 LOC [skip ci] 2026-03-29 05:50:02 +00:00
gitea-analytics-bot
6cb2f50414 Update code stats (2026-03-29T05:50:01.500219Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-29 01:50:01 -04:00
Ares-Bot
75de123f2b chore: sync code velocity to 33870 LOC [skip ci] 2026-03-28 05:50:04 +00:00
gitea-analytics-bot
3758fa83a1 Update code stats (2026-03-28T05:50:01.707549Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-03-28 01:50:01 -04:00
Ares-Bot
c384b33ecf chore: sync code velocity to 33870 LOC [skip ci] 2026-03-27 05:50:04 +00:00
gitea-analytics-bot
a832c99acd Update code stats (2026-03-27T05:50:02.360742Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-27 01:50:02 -04:00
Ares-Bot
00603f8efd chore: sync code velocity to 33080 LOC [skip ci] 2026-03-26 05:50:04 +00:00
gitea-analytics-bot
8864bad706 Update code stats (2026-03-26T05:50:01.968550Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-26 01:50:01 -04:00
Ares-Bot
169834d96e chore: sync code velocity to 32009 LOC [skip ci] 2026-03-25 05:50:04 +00:00
gitea-analytics-bot
5f51237a52 Update code stats (2026-03-25T05:50:01.637157Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-25 01:50:01 -04:00
Ares-Bot
4940bb7c59 chore: sync code velocity to 30011 LOC [skip ci] 2026-03-24 05:50:04 +00:00
gitea-analytics-bot
88e308834a Update code stats (2026-03-24T05:50:01.473444Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-24 01:50:01 -04:00
Ares-Bot
02f6ce4b6d chore: sync code velocity to 30011 LOC [skip ci] 2026-03-23 05:50:04 +00:00
gitea-analytics-bot
b342e07fa9 Update code stats (2026-03-23T05:50:01.855351Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-23 01:50:01 -04:00
Ares-Bot
2ad082b613 chore: sync code velocity to 30011 LOC [skip ci] 2026-03-22 05:50:04 +00:00
gitea-analytics-bot
d3232e9ce4 Update code stats (2026-03-22T05:50:02.282114Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-22 01:50:02 -04:00
Ares-Bot
7c289859ad chore: sync code velocity to 30011 LOC [skip ci] 2026-03-21 21:38:52 +00:00
gitea-analytics-bot
2d643f4ecd Update code stats (2026-03-21T21:38:50.540233Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-21 17:38:50 -04:00
Ares-Bot
e7e0e64caa chore: sync code velocity to 29963 LOC [skip ci] 2026-03-21 15:22:57 +00:00
Ares-Bot
ba512632d9 chore: sync code velocity to 29963 LOC [skip ci] 2026-03-21 05:50:06 +00:00
gitea-analytics-bot
e7e3932944 Update code stats (2026-03-21T05:50:01.642136Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-21 01:50:01 -04:00
Ares-Bot
b3069323c7 chore: sync code velocity to 26626 LOC [skip ci] 2026-03-20 05:50:05 +00:00
gitea-analytics-bot
25a145dfa2 Update code stats (2026-03-20T05:50:02.252977Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-20 01:50:02 -04:00
Ares-Bot
dab398e75d chore: sync code velocity to 26463 LOC [skip ci] 2026-03-19 05:50:03 +00:00
gitea-analytics-bot
98bb579c77 Update code stats (2026-03-19T05:50:01.568759Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-19 01:50:01 -04:00
Ares-Bot
b33f38d271 chore: sync code velocity to 25299 LOC [skip ci] 2026-03-18 05:50:05 +00:00
gitea-analytics-bot
de6175b60a Update code stats (2026-03-18T05:50:01.418528Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-18 01:50:01 -04:00
Ares-Bot
9c8153d681 chore: sync code velocity to 23931 LOC [skip ci] 2026-03-17 05:50:05 +00:00
gitea-analytics-bot
6601f2f290 Update code stats (2026-03-17T05:50:02.055202Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-17 01:50:02 -04:00
Ares-Bot
e98e08bc74 chore: sync code velocity to 22533 LOC [skip ci] 2026-03-16 05:50:05 +00:00
gitea-analytics-bot
e25f9bf6b1 Update code stats (2026-03-16T05:50:02.260135Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-16 01:50:02 -04:00
Ares-Bot
2d01288db8 chore: sync code velocity to 20867 LOC [skip ci] 2026-03-15 05:50:05 +00:00
gitea-analytics-bot
66e689e5c1 Update code stats (2026-03-15T05:50:01.659932Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-15 01:50:01 -04:00
Ares-Bot
610d5b0790 chore: sync code velocity to 20852 LOC [skip ci] 2026-03-15 00:12:05 +00:00
gitea-analytics-bot
fbdbc56179 Update code stats (2026-03-15T00:12:02.085393Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-14 20:12:02 -04:00
Ares-Bot
bc427a7bce chore: sync code velocity to 19218 LOC [skip ci] 2026-03-14 05:50:03 +00:00
gitea-analytics-bot
73e19e10a6 Update code stats (2026-03-14T05:50:01.477133Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-14 01:50:01 -04:00
Ares-Bot
0222d99cfa chore: sync code velocity to 18125 LOC [skip ci] 2026-03-13 05:50:05 +00:00
gitea-analytics-bot
697fd6ffd3 Update code stats (2026-03-13T05:50:01.577205Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-13 01:50:01 -04:00
Ares-Bot
9dc96b7781 chore: sync code velocity to 18125 LOC [skip ci] 2026-03-12 05:50:05 +00:00
gitea-analytics-bot
1623e03dbc Update code stats (2026-03-12T05:50:01.672523Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-12 01:50:01 -04:00
Ares-Bot
0c9075e75f chore: sync code velocity to 18127 LOC [skip ci] 2026-03-11 05:50:05 +00:00
gitea-analytics-bot
73d97d7bbb Update code stats (2026-03-11T05:50:02.084088Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-11 01:50:02 -04:00
Ares-Bot
50c4cdb057 chore: sync code velocity to 17840 LOC [skip ci] 2026-03-10 05:50:05 +00:00
gitea-analytics-bot
9d917da48d Update code stats (2026-03-10T05:50:01.958385Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-10 01:50:01 -04:00
Ares-Bot
3a59ac98bc chore: sync code velocity to 17786 LOC [skip ci] 2026-03-09 05:50:05 +00:00
gitea-analytics-bot
f82c851c17 Update code stats (2026-03-09T05:50:02.410274Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-09 01:50:02 -04:00
Ares-Bot
0cb582ff95 chore: sync code velocity to 17521 LOC [skip ci] 2026-03-08 06:50:05 +00:00
gitea-analytics-bot
1c8f1493a5 Update code stats (2026-03-08T06:50:01.472212Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 2s
2026-03-08 01:50:01 -05:00
Ares-Bot
a20debf2ca chore: sync code velocity to 17333 LOC [skip ci] 2026-03-07 06:50:05 +00:00
gitea-analytics-bot
6274eefb83 Update code stats (2026-03-07T06:50:02.345269Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 2s
2026-03-07 01:50:02 -05:00
Ares-Bot
67c1ab6cf9 chore: sync code velocity to 16318 LOC [skip ci] 2026-03-06 06:50:03 +00:00
gitea-analytics-bot
bde568aba0 Update code stats (2026-03-06T06:50:01.571854Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-03-06 01:50:01 -05:00
Ares-Bot
62e7827600 chore: sync code velocity to 15296 LOC [skip ci] 2026-03-05 06:50:05 +00:00
gitea-analytics-bot
30cf3ad669 Update code stats (2026-03-05T06:50:01.517209Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-05 01:50:01 -05:00
Ares-Bot
7664b40eb2 chore: sync code velocity to 15296 LOC [skip ci] 2026-03-04 06:50:05 +00:00
gitea-analytics-bot
1b6ccead88 Update code stats (2026-03-04T06:50:01.852060Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-04 01:50:01 -05:00
Ares-Bot
b6eb98e181 chore: sync code velocity to 15248 LOC [skip ci] 2026-03-03 06:50:03 +00:00
gitea-analytics-bot
bd14ba3eae Update code stats (2026-03-03T06:50:01.722398Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-03 01:50:01 -05:00
Ares-Bot
180f0e0f6d chore: sync code velocity to 15070 LOC [skip ci] 2026-03-02 06:50:05 +00:00
gitea-analytics-bot
c7f7f71f10 Update code stats (2026-03-02T06:50:02.294582Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-02 01:50:02 -05:00
Ares-Bot
ab4c5a8acd chore: sync code velocity to 14795 LOC [skip ci] 2026-03-01 06:50:05 +00:00
gitea-analytics-bot
44ad5fb09b Update code stats (2026-03-01T06:50:02.068513Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-03-01 01:50:02 -05:00
Ares-Bot
e00bc57923 chore: sync code velocity to 14603 LOC [skip ci] 2026-02-28 06:50:05 +00:00
gitea-analytics-bot
e5aa9e91c7 Update code stats (2026-02-28T06:50:01.757512Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-28 01:50:01 -05:00
Ares-Bot
495db56f4e chore: sync code velocity to 14590 LOC [skip ci] 2026-02-27 06:50:05 +00:00
gitea-analytics-bot
c305700910 Update code stats (2026-02-27T06:50:02.336597Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-27 01:50:02 -05:00
Ares-Bot
156ee2f439 chore: sync code velocity to 14504 LOC [skip ci] 2026-02-26 06:50:03 +00:00
gitea-analytics-bot
99c83ec9d1 Update code stats (2026-02-26T06:50:01.784899Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-26 01:50:01 -05:00
Ares-Bot
c14e5a0572 chore: sync code velocity to 14260 LOC [skip ci] 2026-02-25 06:50:05 +00:00
gitea-analytics-bot
bad17266b4 Update code stats (2026-02-25T06:50:01.774338Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-25 01:50:01 -05:00
Ares-Bot
b4804c5114 chore: sync code velocity to 14150 LOC [skip ci] 2026-02-22 06:50:05 +00:00
gitea-analytics-bot
642f9ac957 Update code stats (2026-02-22T06:50:02.067220Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-02-22 01:50:02 -05:00
Ares-Bot
4d2e96a7b8 chore: sync code velocity to 13845 LOC [skip ci] 2026-02-21 06:50:04 +00:00
gitea-analytics-bot
ac9ca4ce99 Update code stats (2026-02-21T06:50:01.453149Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-21 01:50:01 -05:00
Ares-Bot
d4f6cb0a95 chore: sync code velocity to 11893 LOC [skip ci] 2026-02-12 06:50:04 +00:00
gitea-analytics-bot
a78d30c9c1 Update code stats (2026-02-12T06:50:02.166115Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-12 01:50:02 -05:00
Ares-Bot
94f51ee2b5 chore: sync code velocity to 11821 LOC [skip ci] 2026-02-11 06:50:04 +00:00
gitea-analytics-bot
38e8cf9305 Update code stats (2026-02-11T06:50:01.584252Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-11 01:50:01 -05:00
Ares-Bot
50dc04f1d5 chore: sync code velocity to 11119 LOC [skip ci] 2026-02-10 06:50:04 +00:00
gitea-analytics-bot
fb4966b212 Update code stats (2026-02-10T06:50:01.849703Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-10 01:50:01 -05:00
Ares-Bot
985d4aa61f chore: sync code velocity to 11117 LOC [skip ci] 2026-02-09 06:50:04 +00:00
gitea-analytics-bot
8ba5fe5af5 Update code stats (2026-02-09T06:50:01.860014Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-09 01:50:01 -05:00
Ares-Bot
3c5701c767 chore: sync code velocity to 11078 LOC [skip ci] 2026-02-08 06:50:04 +00:00
gitea-analytics-bot
15607cb7f6 Update code stats (2026-02-08T06:50:02.157691Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-08 01:50:02 -05:00
Ares-Bot
c865d4943f chore: sync code velocity to 11064 LOC [skip ci] 2026-02-07 06:50:06 +00:00
gitea-analytics-bot
921a1d9e30 Update code stats (2026-02-07T06:50:02.211753Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-07 01:50:02 -05:00
Ares-Bot
b856e6ad68 chore: sync code velocity to 11064 LOC [skip ci] 2026-02-06 06:50:04 +00:00
gitea-analytics-bot
cabe5cb61a Update code stats (2026-02-06T06:50:01.912208Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-06 01:50:01 -05:00
Ares-Bot
631df5c7fb chore: sync code velocity to 11051 LOC [skip ci] 2026-02-05 06:50:04 +00:00
gitea-analytics-bot
9ff5da4f16 Update code stats (2026-02-05T06:50:01.431360Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-05 01:50:01 -05:00
Ares-Bot
9ac20faeea chore: sync code velocity to 11033 LOC [skip ci] 2026-02-04 06:50:04 +00:00
gitea-analytics-bot
61ee4388d8 Update code stats (2026-02-04T06:50:02.093103Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-04 01:50:02 -05:00
Ares-Bot
0904f4f95e chore: sync code velocity to 11005 LOC [skip ci] 2026-02-03 06:50:04 +00:00
gitea-analytics-bot
5146b1dc3e Update code stats (2026-02-03T06:50:01.522100Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-03 01:50:01 -05:00
Ares-Bot
33019b3890 chore: sync code velocity to 11005 LOC [skip ci] 2026-02-02 06:50:02 +00:00
gitea-analytics-bot
800a898e97 Update code stats (2026-02-02T06:50:01.573532Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-02 01:50:01 -05:00
Ares-Bot
4f7ce69c03 chore: sync code velocity to 11005 LOC [skip ci] 2026-02-01 06:50:04 +00:00
gitea-analytics-bot
297160bc41 Update code stats (2026-02-01T06:50:02.241333Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-02-01 01:50:02 -05:00
Ares-Bot
a7d65c5785 chore: sync code velocity to 11005 LOC [skip ci] 2026-01-31 22:14:20 +00:00
gitea-analytics-bot
12a15b1d26 Update code stats (2026-01-31T22:14:18.422421Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-31 17:14:18 -05:00
Ares-Bot
a439697617 chore: sync code velocity to 11005 LOC [skip ci] 2026-01-31 22:12:20 +00:00
gitea-analytics-bot
2f05e44737 Update code stats (2026-01-31T22:12:18.299817Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-31 17:12:18 -05:00
Ares-Bot
3d785c1d49 chore: sync code velocity to 11001 LOC [skip ci] 2026-01-31 02:20:48 +00:00
gitea-analytics-bot
2d279824ec Update code stats (2026-01-31T02:20:46.850329Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-30 21:20:46 -05:00
Ares-Bot
245ab17c29 chore: sync code velocity to 11025 LOC [skip ci] 2026-01-30 19:51:23 +00:00
gitea-analytics-bot
e6337caf0d Update code stats (2026-01-30T19:51:20.107027Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-30 14:51:20 -05:00
Ares-Bot
b3cc608eb3 chore: sync code velocity to 10870 LOC [skip ci] 2026-01-29 15:19:35 +00:00
gitea-analytics-bot
d6fe08410d Update code stats (2026-01-29T15:19:33.315792Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-29 10:19:33 -05:00
Ares-Bot
6dbd968bd7 chore: sync code velocity to 10786 LOC [skip ci] 2026-01-29 06:50:04 +00:00
gitea-analytics-bot
093e223107 Update code stats (2026-01-29T06:50:02.119463Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-29 01:50:02 -05:00
Ares-Bot
b2403d8147 chore: sync code velocity to 10786 LOC [skip ci] 2026-01-28 15:38:59 +00:00
gitea-analytics-bot
9a91b38003 Update code stats (2026-01-28T15:38:55.976435Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-28 10:38:55 -05:00
Ares-Bot
667545c601 chore: sync code velocity to 10533 LOC [skip ci] 2026-01-28 06:50:05 +00:00
gitea-analytics-bot
69161411aa Update code stats (2026-01-28T06:50:02.377321Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-28 01:50:02 -05:00
Ares-Bot
083c59bfa7 chore: sync code velocity to 10533 LOC [skip ci] 2026-01-27 06:50:05 +00:00
gitea-analytics-bot
3596101494 Update code stats (2026-01-27T06:50:01.964192Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-27 01:50:01 -05:00
Ares-Bot
a7cee2a6a5 chore: sync code velocity to 10533 LOC [skip ci] 2026-01-27 03:55:53 +00:00
gitea-analytics-bot
f1a874919e Update code stats (2026-01-27T03:55:50.417986Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-26 22:55:50 -05:00
c7656f6220 update: change watchlist duration from 30d for 7d (quickly accumulating) 2026-01-26 22:12:18 +00:00
Ares-Bot
be0c9b003a chore: sync code velocity to 10329 LOC [skip ci] 2026-01-26 14:49:19 +00:00
gitea-analytics-bot
52b196ed56 Update code stats (2026-01-26T14:49:15.649212Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-26 09:49:15 -05:00
Ares-Bot
6312f23fc0 chore: sync code velocity to 10285 LOC [skip ci] 2026-01-26 06:50:05 +00:00
gitea-analytics-bot
351fa62a27 Update code stats (2026-01-26T06:50:02.045361Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-26 01:50:02 -05:00
Ares-Bot
c406c9dd79 chore: sync code velocity to 10285 LOC [skip ci] 2026-01-25 06:50:05 +00:00
gitea-analytics-bot
543ab4e31b Update code stats (2026-01-25T06:50:02.277049Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-25 01:50:02 -05:00
Ares-Bot
1316d978bf chore: sync code velocity to 10285 LOC [skip ci] 2026-01-24 16:47:07 +00:00
gitea-analytics-bot
9320dd3835 Update code stats (2026-01-24T16:47:05.638087Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-24 11:47:05 -05:00
Ares-Bot
6acfe121ce chore: sync code velocity to 10250 LOC [skip ci] 2026-01-24 06:50:05 +00:00
gitea-analytics-bot
dd77951dbb Update code stats (2026-01-24T06:50:01.589533Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 1s
2026-01-24 01:50:01 -05:00
Ares-Bot
10fcdc2b1d chore: sync code velocity to 10250 LOC [skip ci] 2026-01-23 17:22:50 +00:00
gitea-analytics-bot
3454ebc1aa Update code stats (2026-01-23T17:22:46.475883Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-23 12:22:46 -05:00
Ares-Bot
5267a39d51 chore: sync code velocity to 10250 LOC [skip ci] 2026-01-23 06:50:04 +00:00
gitea-analytics-bot
e24fd97fc8 Update code stats (2026-01-23T06:50:01.905436Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-23 01:50:01 -05:00
Ares-Bot
663ae0acb5 chore: sync code velocity to 10250 LOC [skip ci] 2026-01-23 01:28:22 +00:00
gitea-analytics-bot
76f61556cb Update code stats (2026-01-23T01:28:18.762090Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-22 20:28:18 -05:00
Ares-Bot
7c8512a046 chore: sync code velocity to 9966 LOC [skip ci] 2026-01-22 15:35:52 +00:00
gitea-analytics-bot
fd414f0933 Update code stats (2026-01-22T15:35:48.549031Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-22 10:35:48 -05:00
cfcf44d04b feat: implement automated code velocity and LOC tracking 2026-01-21 23:20:51 -05:00
Ares-Bot
9ab3da8658 chore: sync code velocity to LOC [skip ci] 2026-01-21 18:50:18 +00:00
gitea-analytics-bot
5db18aadde Update code stats (2026-01-21T18:50:15.978917Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-21 13:50:15 -05:00
gitea-analytics-bot
8e3a750cd1 Update code stats (2026-01-21T06:50:01.549935Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-21 01:50:01 -05:00
Ares-Bot
9de5f4517e chore: sync code velocity to LOC [skip ci] 2026-01-21 03:48:56 +00:00
gitea-analytics-bot
2d1c26f9df Update code stats (2026-01-21T03:48:52.962684Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-20 22:48:52 -05:00
Ares-Bot
bd578f56d3 chore: sync code velocity to LOC [skip ci] 2026-01-21 03:46:30 +00:00
gitea-analytics-bot
fa58de7ff5 Update code stats (2026-01-21T03:46:26.795090Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-20 22:46:26 -05:00
gitea-analytics-bot
f94393010a fix: sync with upstream before making changes 2026-01-20 22:43:19 -05:00
gitea-analytics-bot
56d522d3a6 Update code stats (2026-01-21T03:35:03.582696Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-20 22:35:03 -05:00
gitea-analytics-bot
e26688bb23 Update code stats (2026-01-21T03:34:19.151024Z) 2026-01-20 22:34:19 -05:00
patrick
93fff0c77a Update code stats (2026-01-21T03:27:27.122709Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-20 22:27:27 -05:00
patrick
6fb25316b6 Update code stats (2026-01-21T02:45:24.787266Z) 2026-01-20 22:27:27 -05:00
Ares-Bot
2afb391e20 chore: sync code velocity to LOC [skip ci] 2026-01-21 02:43:48 +00:00
patrick
6c4e08a39d Update code stats (2026-01-21T02:43:45.909627Z)
All checks were successful
Update Code Statistics / sync-stats (push) Successful in 0s
2026-01-20 21:43:45 -05:00
1342dfb64e fix: update GitHub Actions workflow ensuring unicode is used 2026-01-20 20:37:35 -05:00
Ares-Bot
0bc85d90ac chore: sync code velocity to LOC [skip ci] 2026-01-21 00:58:20 +00:00
76648f1166 cleanup: small edit to README 2026-01-20 19:57:25 -05:00
6164fedfb8 Revert "docs: quick character fix"
This reverts commit 894088d297.
2026-01-20 19:56:09 -05:00
8 changed files with 2079 additions and 28 deletions

View File

@@ -3,6 +3,7 @@ on:
push: push:
paths: paths:
- 'public/code-stats.json' - 'public/code-stats.json'
- 'public/heatmap.json'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -11,32 +12,51 @@ jobs:
steps: steps:
- name: Manual Internal Checkout - name: Manual Internal Checkout
run: | run: |
git clone http://gitea-http:3000/${{ github.repository }}.git . git clone --filter=blob:none http://gitea-http:3000/${{ github.repository }}.git .
git checkout ${{ github.ref_name }} git checkout ${{ github.ref_name }}
- name: Sync with upstream
- name: Update README
run: | run: |
git fetch origin
git rebase origin/${{ github.ref_name }}
- name: Process Statistics and Update README
run: |
if ! command -v jq &> /dev/null; then
if command -v apk &> /dev/null; then apk add --no-cache jq;
elif command -v apt-get &> /dev/null; then apt-get update && apt-get install -y jq; fi
fi
if ! command -v python3 &> /dev/null; then
if command -v apk &> /dev/null; then apk add --no-cache python3;
elif command -v apt-get &> /dev/null; then apt-get update && apt-get install -y python3; fi
fi
# Use our own heatmap.json instead of Gitea API
HEATMAP_FILE="public/heatmap.json"
TOTAL_COMMITS=$(jq '[.days[] | .count] | add' "$HEATMAP_FILE")
echo "Global Commit Velocity: $TOTAL_COMMITS"
echo "COMMIT_VELOCITY=$TOTAL_COMMITS" >> $GITHUB_ENV
LOC=$(grep '"total_code"' public/code-stats.json | sed 's/[^0-9]*//g') LOC=$(grep '"total_code"' public/code-stats.json | sed 's/[^0-9]*//g')
FORMATTED_LOC=$(printf "%'d" $LOC) FORMATTED_LOC=$(printf "%'d" $LOC)
echo "FORMATTED_LOC=$FORMATTED_LOC" >> $GITHUB_ENV
echo "Injecting $FORMATTED_LOC into README.template..." python3 scripts/generate_heatmap_svg.py public/heatmap.json public/heatmap.svg
sed "s/REPLACE_ME_LOC/$FORMATTED_LOC/g" README.template > README.md sed "s/REPLACE_ME_LOC/$FORMATTED_LOC/g" README.template | \
sed "s/REPLACE_ME_COMMITS/$TOTAL_COMMITS/g" > README.md
if [ ! -s README.md ]; then
echo "Error: README.md is empty!"
exit 1
fi
- name: Commit and Push - name: Commit and Push
run: | run: |
git config user.name "Ares-Bot" git config user.name "Ares-Bot"
git config user.email "ares@beane.me" git config user.email "ares@beane.me"
git add README.md git add README.md public/heatmap.svg
if git diff --staged --quiet; then if git diff --staged --quiet; then
echo "No changes" echo "No changes to commit"
else else
git commit -m "chore: sync code velocity to $FORMATTED_LOC LOC [skip ci]" git commit -m "chore: sync profile metrics [skip ci]"
git push http://${{ secrets.GITHUB_TOKEN }}@gitea-http:3000/${{ github.repository }}.git ${{ github.ref_name }} git push http://patrick:${{ secrets.PAT_TOKEN }}@gitea-http:3000/${{ github.repository }}.git ${{ github.ref_name }}
fi fi

View File

@@ -7,7 +7,7 @@ My lab functions as a live production environment where threat intelligence, aut
--- ---
## 🛰 The Fleet (10 Nodes) ## 🛰 The Fleet (10 Nodes)
> This environment blends production, research, and continuous experimentation. > This environment blends production, research, and continuous experimentation.
> Availability and controls are intentionally tuned per node role. > Availability and controls are intentionally tuned per node role.
@@ -43,12 +43,12 @@ My lab functions as a live production environment where threat intelligence, aut
- **Retention:** - **Retention:**
- 24 hours for lower confidence scenarios - 24 hours for lower confidence scenarios
- 14 days for high-confidence IOCs - 14 days for high-confidence IOCs
- 30 days for offender watchlist - 7 days for offender watchlist
- **Notification:** High-severity events dynamically pushed to Discord. - **Notification:** High-severity events dynamically pushed to Discord.
--- ---
## 🛠 The Arsenal ## 🛠 The Arsenal
**Languages:** Python (Flask, Gunicorn), Bash, JavaScript (React, Node.js) **Languages:** Python (Flask, Gunicorn), Bash, JavaScript (React, Node.js)
**Infrastructure:** Kubernetes (K8s), Docker, Caddy, NGINX **Infrastructure:** Kubernetes (K8s), Docker, Caddy, NGINX
@@ -72,10 +72,15 @@ Actively used across this environment or in adjacent projects:
## ⚡ Efficiency Metrics ## ⚡ Efficiency Metrics
- **Codebase Growth:** `0` lines of custom code across all our repositories - **Codebase Growth:** `53135` lines of custom code across all our repositories
- **Commit Velocity:** `1006` commits since Jan 1
- **Ares:** Ryzen 9 9950X sustaining ~0.06 load avg while running Gitea and a Kubernetes control plane - **Ares:** Ryzen 9 9950X sustaining ~0.06 load avg while running Gitea and a Kubernetes control plane
- **Resilience:** Automated failover between AWS and peer nodes - **Resilience:** Automated failover between AWS and peer nodes
## 📈 Activity
![Commit heatmap](public/heatmap.svg)
--- ---
### 🧩 Deployment Patterns ### 🧩 Deployment Patterns
@@ -118,4 +123,4 @@ Optimized for sustained workloads and observability aggregation.
- **Sovereignty:** All code, telemetry, and backups remain self-hosted - **Sovereignty:** All code, telemetry, and backups remain self-hosted
- **Backups:** Multiple daily encrypted Borgmatic snapshots shipped offsite - **Backups:** Multiple daily encrypted Borgmatic snapshots shipped offsite
> *"If it's not blocked, it just hasnt found our infrastructure yet."* > *"If it's not blocked, it just hasn't found our infrastructure yet."*

View File

@@ -1,4 +1,4 @@
# 🛡 Patrick Beane # 🛡 Patrick Beane
**SRE | Security Engineer | Self-Hosted Infra & Detection** **SRE | Security Engineer | Self-Hosted Infra & Detection**
@@ -36,14 +36,14 @@ My lab functions as a live production environment where threat intelligence, aut
--- ---
## 🛡 Detection & Response Lifecycle ## 🛡 Detection & Response Lifecycle
- **Triage:** Telemetry ingested from 7 active nodes into the Argus engine. - **Triage:** Telemetry ingested from 7 active nodes into the Argus engine.
- **Escalation:** Post-exploitation indicators (e.g. webshells) trigger immediate `PERM_BAN`. - **Escalation:** Post-exploitation indicators (e.g. webshells) trigger immediate `PERM_BAN`.
- **Retention:** - **Retention:**
- 24 hours for lower confidence scenarios - 24 hours for lower confidence scenarios
- 14 days for high-confidence IOCs - 14 days for high-confidence IOCs
- 30 days for offender watchlist - 7 days for offender watchlist
- **Notification:** High-severity events dynamically pushed to Discord. - **Notification:** High-severity events dynamically pushed to Discord.
--- ---
@@ -73,9 +73,14 @@ Actively used across this environment or in adjacent projects:
## ⚡ Efficiency Metrics ## ⚡ Efficiency Metrics
- **Codebase Growth:** `REPLACE_ME_LOC` lines of custom code across all our repositories - **Codebase Growth:** `REPLACE_ME_LOC` lines of custom code across all our repositories
- **Commit Velocity:** `REPLACE_ME_COMMITS` commits since Jan 1
- **Ares:** Ryzen 9 9950X sustaining ~0.06 load avg while running Gitea and a Kubernetes control plane - **Ares:** Ryzen 9 9950X sustaining ~0.06 load avg while running Gitea and a Kubernetes control plane
- **Resilience:** Automated failover between AWS and peer nodes - **Resilience:** Automated failover between AWS and peer nodes
## 📈 Activity
![Commit heatmap](public/heatmap.svg)
--- ---
### 🧩 Deployment Patterns ### 🧩 Deployment Patterns
@@ -118,4 +123,4 @@ Optimized for sustained workloads and observability aggregation.
- **Sovereignty:** All code, telemetry, and backups remain self-hosted - **Sovereignty:** All code, telemetry, and backups remain self-hosted
- **Backups:** Multiple daily encrypted Borgmatic snapshots shipped offsite - **Backups:** Multiple daily encrypted Borgmatic snapshots shipped offsite
> *"If it's not blocked, it just hasnt found our infrastructure yet."* > *"If it's not blocked, it just hasn't found our infrastructure yet."*

View File

@@ -1,6 +1,6 @@
{ {
"generated_at": "2026-01-20T19:42:04.836106Z", "generated_at": "2026-04-06T05:50:01Z",
"total_code": 13759, "total_code": 53135,
"total_comments": 1132, "total_comments": 4686,
"total_blanks": 1859 "total_blanks": 8984
} }

1469
public/heatmap.json Normal file

File diff suppressed because it is too large Load Diff

392
public/heatmap.svg Normal file
View File

@@ -0,0 +1,392 @@
<svg width="676" height="114" viewBox="0 0 676 114" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Commit activity heatmap">
<style>
text { font: 10px -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; fill: #57606a; }
</style>
<text x="30" y="10">Apr</text>
<text x="66" y="10">May</text>
<text x="126" y="10">Jun</text>
<text x="174" y="10">Jul</text>
<text x="222" y="10">Aug</text>
<text x="282" y="10">Sep</text>
<text x="330" y="10">Oct</text>
<text x="378" y="10">Nov</text>
<text x="438" y="10">Dec</text>
<text x="486" y="10">Jan</text>
<text x="546" y="10">Feb</text>
<text x="594" y="10">Mar</text>
<text x="642" y="10">Apr</text>
<text x="0" y="40">Mon</text>
<text x="0" y="64">Wed</text>
<text x="0" y="88">Fri</text>
<rect x="30" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-06: 0 commits</title></rect>
<rect x="30" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-07: 0 commits</title></rect>
<rect x="30" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-08: 0 commits</title></rect>
<rect x="30" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-09: 0 commits</title></rect>
<rect x="30" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-10: 0 commits</title></rect>
<rect x="30" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-11: 0 commits</title></rect>
<rect x="30" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-12: 0 commits</title></rect>
<rect x="42" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-13: 0 commits</title></rect>
<rect x="42" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-14: 0 commits</title></rect>
<rect x="42" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-15: 0 commits</title></rect>
<rect x="42" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-16: 0 commits</title></rect>
<rect x="42" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-17: 0 commits</title></rect>
<rect x="42" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-18: 0 commits</title></rect>
<rect x="42" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-19: 0 commits</title></rect>
<rect x="54" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-20: 0 commits</title></rect>
<rect x="54" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-21: 0 commits</title></rect>
<rect x="54" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-22: 0 commits</title></rect>
<rect x="54" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-23: 0 commits</title></rect>
<rect x="54" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-24: 0 commits</title></rect>
<rect x="54" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-25: 0 commits</title></rect>
<rect x="54" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-26: 0 commits</title></rect>
<rect x="66" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-27: 0 commits</title></rect>
<rect x="66" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-28: 0 commits</title></rect>
<rect x="66" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-29: 0 commits</title></rect>
<rect x="66" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-04-30: 0 commits</title></rect>
<rect x="66" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-01: 0 commits</title></rect>
<rect x="66" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-02: 0 commits</title></rect>
<rect x="66" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-03: 0 commits</title></rect>
<rect x="78" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-04: 0 commits</title></rect>
<rect x="78" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-05: 0 commits</title></rect>
<rect x="78" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-06: 0 commits</title></rect>
<rect x="78" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-07: 0 commits</title></rect>
<rect x="78" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-08: 0 commits</title></rect>
<rect x="78" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-09: 0 commits</title></rect>
<rect x="78" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-10: 0 commits</title></rect>
<rect x="90" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-11: 0 commits</title></rect>
<rect x="90" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-12: 0 commits</title></rect>
<rect x="90" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-13: 0 commits</title></rect>
<rect x="90" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-14: 0 commits</title></rect>
<rect x="90" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-15: 0 commits</title></rect>
<rect x="90" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-16: 0 commits</title></rect>
<rect x="90" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-17: 0 commits</title></rect>
<rect x="102" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-18: 0 commits</title></rect>
<rect x="102" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-19: 0 commits</title></rect>
<rect x="102" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-20: 0 commits</title></rect>
<rect x="102" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-21: 0 commits</title></rect>
<rect x="102" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-22: 0 commits</title></rect>
<rect x="102" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-23: 0 commits</title></rect>
<rect x="102" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-24: 0 commits</title></rect>
<rect x="114" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-25: 0 commits</title></rect>
<rect x="114" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-26: 0 commits</title></rect>
<rect x="114" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-27: 0 commits</title></rect>
<rect x="114" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-28: 0 commits</title></rect>
<rect x="114" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-29: 0 commits</title></rect>
<rect x="114" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-30: 0 commits</title></rect>
<rect x="114" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-05-31: 0 commits</title></rect>
<rect x="126" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-01: 0 commits</title></rect>
<rect x="126" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-02: 0 commits</title></rect>
<rect x="126" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-03: 0 commits</title></rect>
<rect x="126" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-04: 0 commits</title></rect>
<rect x="126" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-05: 0 commits</title></rect>
<rect x="126" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-06: 0 commits</title></rect>
<rect x="126" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-07: 0 commits</title></rect>
<rect x="138" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-08: 0 commits</title></rect>
<rect x="138" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-09: 0 commits</title></rect>
<rect x="138" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-10: 0 commits</title></rect>
<rect x="138" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-11: 0 commits</title></rect>
<rect x="138" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-12: 0 commits</title></rect>
<rect x="138" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-13: 0 commits</title></rect>
<rect x="138" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-14: 0 commits</title></rect>
<rect x="150" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-15: 0 commits</title></rect>
<rect x="150" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-16: 0 commits</title></rect>
<rect x="150" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-17: 0 commits</title></rect>
<rect x="150" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-18: 0 commits</title></rect>
<rect x="150" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-19: 0 commits</title></rect>
<rect x="150" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-20: 0 commits</title></rect>
<rect x="150" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-21: 0 commits</title></rect>
<rect x="162" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-22: 0 commits</title></rect>
<rect x="162" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-23: 0 commits</title></rect>
<rect x="162" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-24: 0 commits</title></rect>
<rect x="162" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-25: 0 commits</title></rect>
<rect x="162" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-26: 0 commits</title></rect>
<rect x="162" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-27: 0 commits</title></rect>
<rect x="162" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-28: 0 commits</title></rect>
<rect x="174" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-29: 0 commits</title></rect>
<rect x="174" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-06-30: 0 commits</title></rect>
<rect x="174" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-01: 0 commits</title></rect>
<rect x="174" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-02: 0 commits</title></rect>
<rect x="174" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-03: 0 commits</title></rect>
<rect x="174" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-04: 0 commits</title></rect>
<rect x="174" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-05: 0 commits</title></rect>
<rect x="186" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-06: 0 commits</title></rect>
<rect x="186" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-07: 0 commits</title></rect>
<rect x="186" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-08: 0 commits</title></rect>
<rect x="186" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-09: 0 commits</title></rect>
<rect x="186" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-10: 0 commits</title></rect>
<rect x="186" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-11: 0 commits</title></rect>
<rect x="186" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-12: 0 commits</title></rect>
<rect x="198" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-13: 0 commits</title></rect>
<rect x="198" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-14: 0 commits</title></rect>
<rect x="198" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-15: 0 commits</title></rect>
<rect x="198" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-16: 0 commits</title></rect>
<rect x="198" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-17: 0 commits</title></rect>
<rect x="198" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-18: 0 commits</title></rect>
<rect x="198" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-19: 0 commits</title></rect>
<rect x="210" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-20: 0 commits</title></rect>
<rect x="210" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-21: 0 commits</title></rect>
<rect x="210" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-22: 0 commits</title></rect>
<rect x="210" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-23: 0 commits</title></rect>
<rect x="210" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-24: 0 commits</title></rect>
<rect x="210" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-25: 0 commits</title></rect>
<rect x="210" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-26: 0 commits</title></rect>
<rect x="222" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-27: 0 commits</title></rect>
<rect x="222" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-28: 0 commits</title></rect>
<rect x="222" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-29: 0 commits</title></rect>
<rect x="222" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-30: 0 commits</title></rect>
<rect x="222" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-07-31: 0 commits</title></rect>
<rect x="222" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-01: 0 commits</title></rect>
<rect x="222" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-02: 0 commits</title></rect>
<rect x="234" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-03: 0 commits</title></rect>
<rect x="234" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-04: 0 commits</title></rect>
<rect x="234" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-05: 0 commits</title></rect>
<rect x="234" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-06: 0 commits</title></rect>
<rect x="234" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-07: 0 commits</title></rect>
<rect x="234" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-08: 0 commits</title></rect>
<rect x="234" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2025-08-09: 5 commits</title></rect>
<rect x="246" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-10: 0 commits</title></rect>
<rect x="246" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-11: 0 commits</title></rect>
<rect x="246" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-12: 0 commits</title></rect>
<rect x="246" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-13: 0 commits</title></rect>
<rect x="246" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-14: 0 commits</title></rect>
<rect x="246" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-15: 0 commits</title></rect>
<rect x="246" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2025-08-16: 4 commits</title></rect>
<rect x="258" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-17: 0 commits</title></rect>
<rect x="258" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-18: 0 commits</title></rect>
<rect x="258" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-19: 0 commits</title></rect>
<rect x="258" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-20: 0 commits</title></rect>
<rect x="258" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-21: 0 commits</title></rect>
<rect x="258" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-22: 0 commits</title></rect>
<rect x="258" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-23: 0 commits</title></rect>
<rect x="270" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-24: 0 commits</title></rect>
<rect x="270" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-25: 0 commits</title></rect>
<rect x="270" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-26: 0 commits</title></rect>
<rect x="270" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-27: 0 commits</title></rect>
<rect x="270" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-28: 0 commits</title></rect>
<rect x="270" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-29: 0 commits</title></rect>
<rect x="270" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2025-08-30: 1 commit</title></rect>
<rect x="282" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-08-31: 0 commits</title></rect>
<rect x="282" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-01: 0 commits</title></rect>
<rect x="282" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-02: 0 commits</title></rect>
<rect x="282" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-03: 0 commits</title></rect>
<rect x="282" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-04: 0 commits</title></rect>
<rect x="282" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-05: 0 commits</title></rect>
<rect x="282" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-06: 0 commits</title></rect>
<rect x="294" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-07: 0 commits</title></rect>
<rect x="294" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-08: 0 commits</title></rect>
<rect x="294" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-09: 0 commits</title></rect>
<rect x="294" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-10: 0 commits</title></rect>
<rect x="294" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-11: 0 commits</title></rect>
<rect x="294" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-12: 0 commits</title></rect>
<rect x="294" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-13: 0 commits</title></rect>
<rect x="306" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-14: 0 commits</title></rect>
<rect x="306" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-15: 0 commits</title></rect>
<rect x="306" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-16: 0 commits</title></rect>
<rect x="306" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-17: 0 commits</title></rect>
<rect x="306" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-18: 0 commits</title></rect>
<rect x="306" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-19: 0 commits</title></rect>
<rect x="306" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-20: 0 commits</title></rect>
<rect x="318" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-21: 0 commits</title></rect>
<rect x="318" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-22: 0 commits</title></rect>
<rect x="318" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-23: 0 commits</title></rect>
<rect x="318" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-24: 0 commits</title></rect>
<rect x="318" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-25: 0 commits</title></rect>
<rect x="318" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-26: 0 commits</title></rect>
<rect x="318" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-27: 0 commits</title></rect>
<rect x="330" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-28: 0 commits</title></rect>
<rect x="330" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-29: 0 commits</title></rect>
<rect x="330" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-09-30: 0 commits</title></rect>
<rect x="330" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-01: 0 commits</title></rect>
<rect x="330" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-02: 0 commits</title></rect>
<rect x="330" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-03: 0 commits</title></rect>
<rect x="330" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2025-10-04: 6 commits</title></rect>
<rect x="342" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2025-10-05: 5 commits</title></rect>
<rect x="342" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-06: 0 commits</title></rect>
<rect x="342" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-07: 0 commits</title></rect>
<rect x="342" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-08: 0 commits</title></rect>
<rect x="342" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-09: 0 commits</title></rect>
<rect x="342" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-10: 0 commits</title></rect>
<rect x="342" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-11: 0 commits</title></rect>
<rect x="354" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-12: 0 commits</title></rect>
<rect x="354" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-13: 0 commits</title></rect>
<rect x="354" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-14: 0 commits</title></rect>
<rect x="354" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-15: 0 commits</title></rect>
<rect x="354" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-16: 0 commits</title></rect>
<rect x="354" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-17: 0 commits</title></rect>
<rect x="354" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-18: 0 commits</title></rect>
<rect x="366" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-19: 0 commits</title></rect>
<rect x="366" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-20: 0 commits</title></rect>
<rect x="366" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-21: 0 commits</title></rect>
<rect x="366" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-22: 0 commits</title></rect>
<rect x="366" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-23: 0 commits</title></rect>
<rect x="366" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-24: 0 commits</title></rect>
<rect x="366" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-25: 0 commits</title></rect>
<rect x="378" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-26: 0 commits</title></rect>
<rect x="378" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-27: 0 commits</title></rect>
<rect x="378" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-28: 0 commits</title></rect>
<rect x="378" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-29: 0 commits</title></rect>
<rect x="378" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-30: 0 commits</title></rect>
<rect x="378" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-10-31: 0 commits</title></rect>
<rect x="378" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-01: 0 commits</title></rect>
<rect x="390" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-02: 0 commits</title></rect>
<rect x="390" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-03: 0 commits</title></rect>
<rect x="390" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-04: 0 commits</title></rect>
<rect x="390" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-05: 0 commits</title></rect>
<rect x="390" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-06: 0 commits</title></rect>
<rect x="390" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-07: 0 commits</title></rect>
<rect x="390" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-08: 0 commits</title></rect>
<rect x="402" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-09: 0 commits</title></rect>
<rect x="402" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-10: 0 commits</title></rect>
<rect x="402" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-11: 0 commits</title></rect>
<rect x="402" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-12: 0 commits</title></rect>
<rect x="402" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-13: 0 commits</title></rect>
<rect x="402" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-14: 0 commits</title></rect>
<rect x="402" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-15: 0 commits</title></rect>
<rect x="414" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-16: 0 commits</title></rect>
<rect x="414" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-17: 0 commits</title></rect>
<rect x="414" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-18: 0 commits</title></rect>
<rect x="414" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-19: 0 commits</title></rect>
<rect x="414" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-20: 0 commits</title></rect>
<rect x="414" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-21: 0 commits</title></rect>
<rect x="414" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-22: 0 commits</title></rect>
<rect x="426" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-23: 0 commits</title></rect>
<rect x="426" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-24: 0 commits</title></rect>
<rect x="426" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-25: 0 commits</title></rect>
<rect x="426" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-26: 0 commits</title></rect>
<rect x="426" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-27: 0 commits</title></rect>
<rect x="426" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-28: 0 commits</title></rect>
<rect x="426" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-29: 0 commits</title></rect>
<rect x="438" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-11-30: 0 commits</title></rect>
<rect x="438" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-01: 0 commits</title></rect>
<rect x="438" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-02: 0 commits</title></rect>
<rect x="438" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-03: 0 commits</title></rect>
<rect x="438" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-04: 0 commits</title></rect>
<rect x="438" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-05: 0 commits</title></rect>
<rect x="438" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-06: 0 commits</title></rect>
<rect x="450" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-07: 0 commits</title></rect>
<rect x="450" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-08: 0 commits</title></rect>
<rect x="450" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-09: 0 commits</title></rect>
<rect x="450" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-10: 0 commits</title></rect>
<rect x="450" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-11: 0 commits</title></rect>
<rect x="450" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-12: 0 commits</title></rect>
<rect x="450" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-13: 0 commits</title></rect>
<rect x="462" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-14: 0 commits</title></rect>
<rect x="462" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-15: 0 commits</title></rect>
<rect x="462" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-16: 0 commits</title></rect>
<rect x="462" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-17: 0 commits</title></rect>
<rect x="462" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-18: 0 commits</title></rect>
<rect x="462" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-19: 0 commits</title></rect>
<rect x="462" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-20: 0 commits</title></rect>
<rect x="474" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-21: 0 commits</title></rect>
<rect x="474" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-22: 0 commits</title></rect>
<rect x="474" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-23: 0 commits</title></rect>
<rect x="474" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-24: 0 commits</title></rect>
<rect x="474" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-25: 0 commits</title></rect>
<rect x="474" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-26: 0 commits</title></rect>
<rect x="474" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-27: 0 commits</title></rect>
<rect x="486" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-28: 0 commits</title></rect>
<rect x="486" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-29: 0 commits</title></rect>
<rect x="486" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-30: 0 commits</title></rect>
<rect x="486" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2025-12-31: 0 commits</title></rect>
<rect x="486" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-01: 0 commits</title></rect>
<rect x="486" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-02: 0 commits</title></rect>
<rect x="486" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-03: 0 commits</title></rect>
<rect x="498" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-04: 0 commits</title></rect>
<rect x="498" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-05: 0 commits</title></rect>
<rect x="498" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-06: 0 commits</title></rect>
<rect x="498" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-07: 0 commits</title></rect>
<rect x="498" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-08: 10 commits</title></rect>
<rect x="498" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-09: 4 commits</title></rect>
<rect x="498" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-01-10: 0 commits</title></rect>
<rect x="510" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-11: 1 commit</title></rect>
<rect x="510" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-12: 9 commits</title></rect>
<rect x="510" y="44" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-13: 11 commits</title></rect>
<rect x="510" y="56" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-14: 23 commits</title></rect>
<rect x="510" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-15: 10 commits</title></rect>
<rect x="510" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-16: 10 commits</title></rect>
<rect x="510" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-17: 3 commits</title></rect>
<rect x="522" y="20" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-18: 23 commits</title></rect>
<rect x="522" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-19: 9 commits</title></rect>
<rect x="522" y="44" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-20: 28 commits</title></rect>
<rect x="522" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-21: 6 commits</title></rect>
<rect x="522" y="68" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-22: 15 commits</title></rect>
<rect x="522" y="80" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-23: 17 commits</title></rect>
<rect x="522" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-01-24: 5 commits</title></rect>
<rect x="534" y="20" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-25: 20 commits</title></rect>
<rect x="534" y="32" width="10" height="10" rx="2" ry="2" fill="#216e39"><title>2026-01-26: 56 commits</title></rect>
<rect x="534" y="44" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-27: 22 commits</title></rect>
<rect x="534" y="56" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-28: 16 commits</title></rect>
<rect x="534" y="68" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-29: 28 commits</title></rect>
<rect x="534" y="80" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-01-30: 16 commits</title></rect>
<rect x="534" y="92" width="10" height="10" rx="2" ry="2" fill="#30a14e"><title>2026-01-31: 31 commits</title></rect>
<rect x="546" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-02-01: 0 commits</title></rect>
<rect x="546" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-02: 2 commits</title></rect>
<rect x="546" y="44" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-03: 1 commit</title></rect>
<rect x="546" y="56" width="10" height="10" rx="2" ry="2" fill="#30a14e"><title>2026-02-04: 40 commits</title></rect>
<rect x="546" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-05: 10 commits</title></rect>
<rect x="546" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-06: 8 commits</title></rect>
<rect x="546" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-07: 1 commit</title></rect>
<rect x="558" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-08: 6 commits</title></rect>
<rect x="558" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-09: 2 commits</title></rect>
<rect x="558" y="44" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-02-10: 27 commits</title></rect>
<rect x="558" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-11: 6 commits</title></rect>
<rect x="558" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-12: 10 commits</title></rect>
<rect x="558" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-13: 2 commits</title></rect>
<rect x="558" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-02-14: 0 commits</title></rect>
<rect x="570" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-15: 1 commit</title></rect>
<rect x="570" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-16: 1 commit</title></rect>
<rect x="570" y="44" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-17: 3 commits</title></rect>
<rect x="570" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-18: 1 commit</title></rect>
<rect x="570" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-19: 4 commits</title></rect>
<rect x="570" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-20: 4 commits</title></rect>
<rect x="570" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-02-21: 0 commits</title></rect>
<rect x="582" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-22: 1 commit</title></rect>
<rect x="582" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-23: 2 commits</title></rect>
<rect x="582" y="44" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-24: 4 commits</title></rect>
<rect x="582" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-25: 5 commits</title></rect>
<rect x="582" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-26: 2 commits</title></rect>
<rect x="582" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-02-27: 9 commits</title></rect>
<rect x="582" y="92" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-02-28: 19 commits</title></rect>
<rect x="594" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-01: 9 commits</title></rect>
<rect x="594" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-02: 10 commits</title></rect>
<rect x="594" y="44" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-03: 2 commits</title></rect>
<rect x="594" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-03-04: 0 commits</title></rect>
<rect x="594" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-05: 14 commits</title></rect>
<rect x="594" y="80" width="10" height="10" rx="2" ry="2" fill="#30a14e"><title>2026-03-06: 29 commits</title></rect>
<rect x="594" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-07: 10 commits</title></rect>
<rect x="606" y="20" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-08: 19 commits</title></rect>
<rect x="606" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-09: 2 commits</title></rect>
<rect x="606" y="44" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-10: 20 commits</title></rect>
<rect x="606" y="56" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-11: 27 commits</title></rect>
<rect x="606" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-03-12: 0 commits</title></rect>
<rect x="606" y="80" width="10" height="10" rx="2" ry="2" fill="#30a14e"><title>2026-03-13: 37 commits</title></rect>
<rect x="606" y="92" width="10" height="10" rx="2" ry="2" fill="#216e39"><title>2026-03-14: 49 commits</title></rect>
<rect x="618" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-15: 9 commits</title></rect>
<rect x="618" y="32" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-16: 17 commits</title></rect>
<rect x="618" y="44" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-17: 21 commits</title></rect>
<rect x="618" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-18: 14 commits</title></rect>
<rect x="618" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-19: 10 commits</title></rect>
<rect x="618" y="80" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-20: 16 commits</title></rect>
<rect x="618" y="92" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-21: 7 commits</title></rect>
<rect x="630" y="20" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-03-22: 0 commits</title></rect>
<rect x="630" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-23: 1 commit</title></rect>
<rect x="630" y="44" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-03-24: 19 commits</title></rect>
<rect x="630" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-25: 9 commits</title></rect>
<rect x="630" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-26: 7 commits</title></rect>
<rect x="630" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-03-27: 0 commits</title></rect>
<rect x="630" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-03-28: 0 commits</title></rect>
<rect x="642" y="20" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-29: 2 commits</title></rect>
<rect x="642" y="32" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-30: 9 commits</title></rect>
<rect x="642" y="44" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-03-31: 6 commits</title></rect>
<rect x="642" y="56" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-04-01: 1 commit</title></rect>
<rect x="642" y="68" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-04-02: 10 commits</title></rect>
<rect x="642" y="80" width="10" height="10" rx="2" ry="2" fill="#9be9a8"><title>2026-04-03: 9 commits</title></rect>
<rect x="642" y="92" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-04-04: 24 commits</title></rect>
<rect x="654" y="20" width="10" height="10" rx="2" ry="2" fill="#40c463"><title>2026-04-05: 22 commits</title></rect>
<rect x="654" y="32" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-04-06: 0 commits</title></rect>
<rect x="654" y="44" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-04-07: 0 commits</title></rect>
<rect x="654" y="56" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-04-08: 0 commits</title></rect>
<rect x="654" y="68" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-04-09: 0 commits</title></rect>
<rect x="654" y="80" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-04-10: 0 commits</title></rect>
<rect x="654" y="92" width="10" height="10" rx="2" ry="2" fill="#ebedf0"><title>2026-04-11: 0 commits</title></rect>
</svg>

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,160 @@
#!/usr/bin/env python3
import json
import math
import sys
from datetime import datetime, timedelta
from pathlib import Path
COLORS = [
"#ebedf0",
"#9be9a8",
"#40c463",
"#30a14e",
"#216e39",
]
CELL = 12
GAP = 2
RECT = CELL - GAP
LEFT_PAD = 30
TOP_PAD = 20
RIGHT_PAD = 10
BOTTOM_PAD = 10
FONT_FAMILY = "-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif"
LABEL_COLOR = "#57606a"
def load_heatmap(path):
with path.open() as handle:
return json.load(handle)
def date_range(start, end):
current = start
while current <= end:
yield current
current += timedelta(days=1)
def previous_sunday(day):
return day - timedelta(days=(day.weekday() + 1) % 7)
def next_saturday(day):
return day + timedelta(days=(5 - day.weekday()) % 7)
def intensity(count, max_count):
if count <= 0 or max_count <= 0:
return 0
return max(1, min(4, math.ceil((count / max_count) * 4)))
def weekday_row(day):
return (day.weekday() + 1) % 7
def month_labels(grid_start, visible_start, grid_end):
labels = []
total_weeks = ((grid_end - grid_start).days // 7) + 1
for week in range(total_weeks):
week_start = grid_start + timedelta(days=week * 7)
week_label = None
for offset in range(7):
day = week_start + timedelta(days=offset)
if day < visible_start:
continue
if week == 0 and week_label is None:
week_label = day.strftime("%b")
if day.day == 1:
week_label = day.strftime("%b")
break
if week_label is not None:
if not labels or labels[-1][1] != week_label:
labels.append((week, week_label))
return labels
def build_rect(day, start, counts, max_count):
date_str = day.isoformat()
count = counts.get(date_str, 0)
level = intensity(count, max_count)
x = LEFT_PAD + (((day - start).days // 7) * CELL)
y = TOP_PAD + (weekday_row(day) * CELL)
commit_label = "commit" if count == 1 else "commits"
return (
f'<rect x="{x}" y="{y}" width="{RECT}" height="{RECT}" rx="2" ry="2" '
f'fill="{COLORS[level]}">'
f"<title>{date_str}: {count} {commit_label}</title>"
"</rect>"
)
def generate_svg(data):
counts = {entry["date"]: entry["count"] for entry in data["days"]}
max_count = data["max_daily_commits"]
start = datetime.strptime(data["from_date"], "%Y-%m-%d").date()
end = datetime.strptime(data["to_date"], "%Y-%m-%d").date()
aligned_start = previous_sunday(start)
aligned_end = next_saturday(end)
weeks = ((aligned_end - aligned_start).days // 7) + 1
width = LEFT_PAD + (weeks * CELL) + RIGHT_PAD
height = TOP_PAD + (7 * CELL) + BOTTOM_PAD
svg = [
f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" '
'xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Commit activity heatmap">',
'<style>',
f'text {{ font: 10px {FONT_FAMILY}; fill: {LABEL_COLOR}; }}',
'</style>',
]
for week, label in month_labels(aligned_start, start, aligned_end):
x = LEFT_PAD + (week * CELL)
svg.append(f'<text x="{x}" y="10">{label}</text>')
for label, row in (("Mon", 1), ("Wed", 3), ("Fri", 5)):
y = TOP_PAD + (row * CELL) + 8
svg.append(f'<text x="0" y="{y}">{label}</text>')
for day in date_range(aligned_start, aligned_end):
svg.append(build_rect(day, aligned_start, counts, max_count))
svg.append("</svg>")
return "\n".join(svg) + "\n"
def main():
if len(sys.argv) not in {2, 3}:
print(
"Usage: generate_heatmap_svg.py <heatmap.json> [output.svg]",
file=sys.stderr,
)
return 1
input_path = Path(sys.argv[1])
output = generate_svg(load_heatmap(input_path))
if len(sys.argv) == 3:
output_path = Path(sys.argv[2])
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(output)
else:
sys.stdout.write(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())