@urmzd/github-insights 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/.githooks/.sr-hooks-hash +1 -0
  2. package/.githooks/commit-msg +3 -0
  3. package/.githooks/pre-commit +3 -0
  4. package/AGENTS.md +32 -19
  5. package/CHANGELOG.md +62 -0
  6. package/CONTRIBUTING.md +18 -19
  7. package/README.md +21 -24
  8. package/action.yml +1 -1
  9. package/assets/insights/index.svg +45 -4
  10. package/assets/insights/metrics-constellation.svg +55 -0
  11. package/assets/insights/metrics-growth.svg +55 -0
  12. package/assets/insights/metrics-heatmap.svg +55 -0
  13. package/assets/insights/metrics-impact.svg +55 -0
  14. package/assets/insights/metrics-rhythm.svg +55 -0
  15. package/assets/insights/metrics-velocity.svg +55 -0
  16. package/examples/classic/README.md +36 -2
  17. package/examples/classic/index.svg +45 -4
  18. package/examples/classic/metrics-constellation.svg +55 -0
  19. package/examples/classic/metrics-growth.svg +55 -0
  20. package/examples/classic/metrics-heatmap.svg +55 -0
  21. package/examples/classic/metrics-impact.svg +55 -0
  22. package/examples/classic/metrics-rhythm.svg +55 -0
  23. package/examples/classic/metrics-velocity.svg +55 -0
  24. package/examples/ecosystem/README.md +39 -28
  25. package/examples/ecosystem/index.svg +45 -4
  26. package/examples/ecosystem/metrics-constellation.svg +55 -0
  27. package/examples/ecosystem/metrics-growth.svg +55 -0
  28. package/examples/ecosystem/metrics-heatmap.svg +55 -0
  29. package/examples/ecosystem/metrics-impact.svg +55 -0
  30. package/examples/ecosystem/metrics-rhythm.svg +55 -0
  31. package/examples/ecosystem/metrics-velocity.svg +55 -0
  32. package/examples/minimal/README.md +36 -2
  33. package/examples/minimal/index.svg +45 -4
  34. package/examples/minimal/metrics-constellation.svg +55 -0
  35. package/examples/minimal/metrics-growth.svg +55 -0
  36. package/examples/minimal/metrics-heatmap.svg +55 -0
  37. package/examples/minimal/metrics-impact.svg +55 -0
  38. package/examples/minimal/metrics-rhythm.svg +55 -0
  39. package/examples/minimal/metrics-velocity.svg +55 -0
  40. package/examples/modern/README.md +62 -50
  41. package/examples/modern/index.svg +45 -4
  42. package/examples/modern/metrics-constellation.svg +55 -0
  43. package/examples/modern/metrics-growth.svg +55 -0
  44. package/examples/modern/metrics-heatmap.svg +55 -0
  45. package/examples/modern/metrics-impact.svg +55 -0
  46. package/examples/modern/metrics-rhythm.svg +55 -0
  47. package/examples/modern/metrics-velocity.svg +55 -0
  48. package/llms.txt +4 -4
  49. package/package.json +1 -1
  50. package/skills/github-insights/SKILL.md +35 -81
  51. package/sr.yaml +9 -0
  52. package/src/api.ts +2 -140
  53. package/src/components/contribution-heatmap.tsx +43 -0
  54. package/src/components/contribution-rhythm.tsx +152 -0
  55. package/src/components/full-svg.test.tsx +4 -1
  56. package/src/components/full-svg.tsx +14 -7
  57. package/src/components/growth-arc.tsx +119 -0
  58. package/src/components/impact-trail.tsx +90 -0
  59. package/src/components/language-velocity.tsx +181 -0
  60. package/src/components/project-constellation.tsx +97 -0
  61. package/src/components/section.test.tsx +5 -3
  62. package/src/components/section.tsx +5 -13
  63. package/src/components/style-defs.tsx +44 -3
  64. package/src/index.ts +28 -47
  65. package/src/metrics.test.ts +50 -57
  66. package/src/metrics.ts +277 -95
  67. package/src/readme.test.ts +2 -4
  68. package/src/templates.test.ts +19 -16
  69. package/src/templates.ts +30 -16
  70. package/src/theme.ts +11 -1
  71. package/src/types.ts +34 -7
  72. package/assets/insights/metrics-calendar.svg +0 -14
  73. package/assets/insights/metrics-complexity.svg +0 -14
  74. package/assets/insights/metrics-contributions.svg +0 -14
  75. package/assets/insights/metrics-expertise.svg +0 -14
  76. package/assets/insights/metrics-languages.svg +0 -14
  77. package/assets/insights/metrics-pulse.svg +0 -14
  78. package/examples/classic/metrics-calendar.svg +0 -14
  79. package/examples/classic/metrics-complexity.svg +0 -14
  80. package/examples/classic/metrics-contributions.svg +0 -14
  81. package/examples/classic/metrics-expertise.svg +0 -14
  82. package/examples/classic/metrics-languages.svg +0 -14
  83. package/examples/classic/metrics-pulse.svg +0 -14
  84. package/examples/ecosystem/metrics-calendar.svg +0 -14
  85. package/examples/ecosystem/metrics-complexity.svg +0 -14
  86. package/examples/ecosystem/metrics-contributions.svg +0 -14
  87. package/examples/ecosystem/metrics-expertise.svg +0 -14
  88. package/examples/ecosystem/metrics-languages.svg +0 -14
  89. package/examples/ecosystem/metrics-pulse.svg +0 -14
  90. package/examples/minimal/metrics-calendar.svg +0 -14
  91. package/examples/minimal/metrics-complexity.svg +0 -14
  92. package/examples/minimal/metrics-contributions.svg +0 -14
  93. package/examples/minimal/metrics-expertise.svg +0 -14
  94. package/examples/minimal/metrics-languages.svg +0 -14
  95. package/examples/minimal/metrics-pulse.svg +0 -14
  96. package/examples/modern/metrics-calendar.svg +0 -14
  97. package/examples/modern/metrics-complexity.svg +0 -14
  98. package/examples/modern/metrics-contributions.svg +0 -14
  99. package/examples/modern/metrics-expertise.svg +0 -14
  100. package/examples/modern/metrics-languages.svg +0 -14
  101. package/examples/modern/metrics-pulse.svg +0 -14
  102. package/src/components/bar-chart.test.tsx +0 -38
  103. package/src/components/bar-chart.tsx +0 -54
  104. package/src/components/contribution-calendar.test.tsx +0 -44
  105. package/src/components/contribution-calendar.tsx +0 -94
  106. package/src/components/contribution-cards.test.tsx +0 -36
  107. package/src/components/contribution-cards.tsx +0 -58
  108. package/src/components/donut-chart.test.tsx +0 -36
  109. package/src/components/donut-chart.tsx +0 -102
  110. package/src/components/project-cards.test.tsx +0 -46
  111. package/src/components/project-cards.tsx +0 -66
  112. package/src/components/stat-cards.test.tsx +0 -32
  113. package/src/components/stat-cards.tsx +0 -57
  114. package/src/components/tech-highlights.test.tsx +0 -63
  115. package/src/components/tech-highlights.tsx +0 -109
  116. package/teasr.toml +0 -14
@@ -1,14 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="808" height="197" viewBox="0 0 808 197"><defs><style>
2
- .t { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; }
3
- .t-h { font-size: 13px; fill: #c9d1d9; letter-spacing: 1.5px; font-weight: 600; }
4
- .t-sub { font-size: 11px; fill: #6e7681; }
5
- .t-label { font-size: 12px; fill: #8b949e; }
6
- .t-value { font-size: 11px; fill: #6e7681; }
7
- .t-subhdr { font-size: 11px; fill: #8b949e; letter-spacing: 1px; font-weight: 600; }
8
- .t-stat-label { font-size: 10px; fill: #8b949e; font-weight: 600; }
9
- .t-stat-value { font-size: 22px; font-weight: 700; }
10
- .t-card-title { font-size: 12px; fill: #58a6ff; font-weight: 700; }
11
- .t-card-detail { font-size: 11px; fill: #8b949e; }
12
- .t-pill { font-size: 11px; font-weight: 600; }
13
- .t-bullet { font-size: 12px; fill: #c9d1d9; }
14
- </style></defs><rect width="808" height="197" rx="12" fill="#0d1117"/><text x="24" y="40" class="t t-h">CONTRIBUTION CALENDAR</text><text x="24" y="56" class="t t-sub">1,217 contributions in the last year</text><text x="54" y="77" class="t t-value">Mar</text><text x="93" y="77" class="t t-value">Apr</text><text x="145" y="77" class="t t-value">May</text><text x="210" y="77" class="t t-value">Jun</text><text x="262" y="77" class="t t-value">Jul</text><text x="314" y="77" class="t t-value">Aug</text><text x="379" y="77" class="t t-value">Sep</text><text x="431" y="77" class="t t-value">Oct</text><text x="483" y="77" class="t t-value">Nov</text><text x="548" y="77" class="t t-value">Dec</text><text x="600" y="77" class="t t-value">Jan</text><text x="665" y="77" class="t t-value">Feb</text><text x="717" y="77" class="t t-value">Mar</text><text x="24" y="105" class="t t-value">Mon</text><text x="24" y="131" class="t t-value">Wed</text><text x="24" y="157" class="t t-value">Fri</text><rect x="54" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="54" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="54" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="54" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="54" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="54" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="67" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="80" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="93" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="106" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="119" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="132" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="145" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="158" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="158" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="158" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="158" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="158" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="158" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="158" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="171" y="82" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="171" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="171" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="171" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="171" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="171" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="171" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="184" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="197" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="210" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="223" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="236" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="249" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="249" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="249" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="249" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="249" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="249" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="249" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="262" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="262" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="262" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="262" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="262" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="262" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="262" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="275" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="288" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="301" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="301" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="301" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="301" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="301" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="301" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="301" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="314" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="327" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="340" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="353" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="366" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="379" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="379" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="379" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="379" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="379" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="379" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="379" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="392" y="82" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="392" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="392" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="392" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="392" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="392" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="392" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="405" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="418" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="431" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="444" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="457" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="470" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="470" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="470" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="470" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="470" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="470" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="470" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="483" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="483" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="483" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="483" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="483" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="483" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="483" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="496" y="82" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="496" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="496" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="496" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="496" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="496" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="496" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="509" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="522" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="522" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="522" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="522" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="522" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="522" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="522" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="535" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="535" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="535" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="535" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="535" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="535" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="535" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="548" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="548" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="548" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="548" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="548" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="548" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="548" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="561" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="561" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="561" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="561" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="561" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="561" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="561" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="574" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="108" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="587" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="600" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="600" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="600" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="600" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="600" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="600" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="600" y="160" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="613" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="613" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="613" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="613" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="613" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="613" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="613" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="626" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="626" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="626" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="626" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="626" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="626" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="626" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="639" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="639" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="639" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="639" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="639" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="639" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="639" y="160" width="11" height="11" rx="2" fill="#216e39"/><rect x="652" y="82" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="652" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="652" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="652" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="652" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="652" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="652" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="665" y="82" width="11" height="11" rx="2" fill="#216e39"/><rect x="665" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="665" y="108" width="11" height="11" rx="2" fill="#40c463"/><rect x="665" y="121" width="11" height="11" rx="2" fill="#216e39"/><rect x="665" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="665" y="147" width="11" height="11" rx="2" fill="#40c463"/><rect x="665" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="678" y="82" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="678" y="95" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="678" y="108" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="678" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="678" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="678" y="147" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="678" y="160" width="11" height="11" rx="2" fill="#40c463"/><rect x="691" y="82" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="691" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="691" y="108" width="11" height="11" rx="2" fill="#30a14e"/><rect x="691" y="121" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="691" y="134" width="11" height="11" rx="2" fill="#40c463"/><rect x="691" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="691" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="704" y="82" width="11" height="11" rx="2" fill="#40c463"/><rect x="704" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="704" y="108" width="11" height="11" rx="2" fill="#30a14e"/><rect x="704" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="704" y="134" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="704" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="704" y="160" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="717" y="82" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="717" y="95" width="11" height="11" rx="2" fill="#40c463"/><rect x="717" y="108" width="11" height="11" rx="2" fill="#40c463"/><rect x="717" y="121" width="11" height="11" rx="2" fill="#ebedf0"/><rect x="717" y="134" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="717" y="147" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="717" y="160" width="11" height="11" rx="2" fill="#216e39"/><rect x="730" y="82" width="11" height="11" rx="2" fill="#216e39"/><rect x="730" y="95" width="11" height="11" rx="2" fill="#9be9a8"/><rect x="730" y="108" width="11" height="11" rx="2" fill="#ebedf0"/></svg>
@@ -1,14 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="808" height="390" viewBox="0 0 808 390"><defs><style>
2
- .t { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; }
3
- .t-h { font-size: 13px; fill: #c9d1d9; letter-spacing: 1.5px; font-weight: 600; }
4
- .t-sub { font-size: 11px; fill: #6e7681; }
5
- .t-label { font-size: 12px; fill: #8b949e; }
6
- .t-value { font-size: 11px; fill: #6e7681; }
7
- .t-subhdr { font-size: 11px; fill: #8b949e; letter-spacing: 1px; font-weight: 600; }
8
- .t-stat-label { font-size: 10px; fill: #8b949e; font-weight: 600; }
9
- .t-stat-value { font-size: 22px; font-weight: 700; }
10
- .t-card-title { font-size: 12px; fill: #58a6ff; font-weight: 700; }
11
- .t-card-detail { font-size: 11px; fill: #8b949e; }
12
- .t-pill { font-size: 11px; font-weight: 600; }
13
- .t-bullet { font-size: 12px; fill: #c9d1d9; }
14
- </style></defs><rect width="808" height="390" rx="12" fill="#0d1117"/><text x="24" y="40" class="t t-h">SIGNATURE PROJECTS</text><text x="24" y="56" class="t t-sub">Top projects by technical complexity</text><rect x="24" y="66" width="760" height="52" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="66" width="4" height="52" rx="2" fill="#58a6ff"/><text x="40" y="84" class="t t-card-title">resume-generator</text><text x="768" y="84" class="t t-value" text-anchor="end">★ 10</text><text x="40" y="100" class="t t-card-detail">A data-driven CLI tool that converts YAML/JSON/TOML resume data into polished PDFs, DOCX,…</text><rect x="24" y="128" width="760" height="52" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="128" width="4" height="52" rx="2" fill="#3fb950"/><text x="40" y="146" class="t t-card-title">linear-gp</text><text x="768" y="146" class="t t-value" text-anchor="end">★ 2</text><text x="40" y="162" class="t t-card-detail">A production-grade Rust framework for Linear Genetic Programming research, featuring modu…</text><rect x="24" y="190" width="760" height="52" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="190" width="4" height="52" rx="2" fill="#d29922"/><text x="40" y="208" class="t t-card-title">zigbee-rest</text><text x="768" y="208" class="t t-value" text-anchor="end">★ 0</text><text x="40" y="224" class="t t-card-detail">Local-first, privacy-focused smart home control system. Manage Zigbee devices through a R…</text><rect x="24" y="252" width="760" height="52" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="252" width="4" height="52" rx="2" fill="#f85149"/><text x="40" y="270" class="t t-card-title">urmzd.com</text><text x="768" y="270" class="t t-value" text-anchor="end">★ 0</text><text x="40" y="286" class="t t-card-detail">Personal website and blog built with Astro, TypeScript, and MDX—featuring fast static sit…</text><rect x="24" y="314" width="760" height="52" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="314" width="4" height="52" rx="2" fill="#bc8cff"/><text x="40" y="332" class="t t-card-title">resume-generator-app</text><text x="768" y="332" class="t t-value" text-anchor="end">★ 0</text><text x="40" y="348" class="t t-card-detail">A native desktop app for building polished resumes with live PDF preview, template galler…</text></svg>
@@ -1,14 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="808" height="186" viewBox="0 0 808 186"><defs><style>
2
- .t { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; }
3
- .t-h { font-size: 13px; fill: #c9d1d9; letter-spacing: 1.5px; font-weight: 600; }
4
- .t-sub { font-size: 11px; fill: #6e7681; }
5
- .t-label { font-size: 12px; fill: #8b949e; }
6
- .t-value { font-size: 11px; fill: #6e7681; }
7
- .t-subhdr { font-size: 11px; fill: #8b949e; letter-spacing: 1px; font-weight: 600; }
8
- .t-stat-label { font-size: 10px; fill: #8b949e; font-weight: 600; }
9
- .t-stat-value { font-size: 22px; font-weight: 700; }
10
- .t-card-title { font-size: 12px; fill: #58a6ff; font-weight: 700; }
11
- .t-card-detail { font-size: 11px; fill: #8b949e; }
12
- .t-pill { font-size: 11px; font-weight: 600; }
13
- .t-bullet { font-size: 12px; fill: #c9d1d9; }
14
- </style></defs><rect width="808" height="186" rx="12" fill="#0d1117"/><text x="24" y="40" class="t t-h">OPEN SOURCE CONTRIBUTIONS</text><text x="24" y="56" class="t t-sub">External repositories contributed to (all time)</text><rect x="24" y="66" width="760" height="44" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="66" width="4" height="44" rx="2" fill="#58a6ff"/><text x="40" y="84" class="t t-card-title">MathisWellmann/gym-rs</text><text x="40" y="100" class="t t-card-detail">★ 127 · Rust</text><rect x="24" y="118" width="760" height="44" rx="6" fill="#161b22" stroke="#30363d" stroke-width="1"/><rect x="24" y="118" width="4" height="44" rx="2" fill="#3fb950"/><text x="40" y="136" class="t t-card-title">getzep/graphiti</text><text x="40" y="152" class="t t-card-detail">★ 23,825 · Python</text></svg>
@@ -1,14 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="808" height="550" viewBox="0 0 808 550"><defs><style>
2
- .t { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; }
3
- .t-h { font-size: 13px; fill: #c9d1d9; letter-spacing: 1.5px; font-weight: 600; }
4
- .t-sub { font-size: 11px; fill: #6e7681; }
5
- .t-label { font-size: 12px; fill: #8b949e; }
6
- .t-value { font-size: 11px; fill: #6e7681; }
7
- .t-subhdr { font-size: 11px; fill: #8b949e; letter-spacing: 1px; font-weight: 600; }
8
- .t-stat-label { font-size: 10px; fill: #8b949e; font-weight: 600; }
9
- .t-stat-value { font-size: 22px; font-weight: 700; }
10
- .t-card-title { font-size: 12px; fill: #58a6ff; font-weight: 700; }
11
- .t-card-detail { font-size: 11px; fill: #8b949e; }
12
- .t-pill { font-size: 11px; font-weight: 600; }
13
- .t-bullet { font-size: 12px; fill: #c9d1d9; }
14
- </style></defs><rect width="808" height="550" rx="12" fill="#0d1117"/><text x="24" y="40" class="t t-h">EXPERTISE</text><text x="24" y="56" class="t t-sub">Curated from dependencies, topics, and languages via AI analysis</text><text x="24" y="80" class="t t-subhdr">AI &amp; MACHINE LEARNING</text><rect x="24" y="92" width="700" height="18" rx="4" fill="#58a6ff" fill-opacity="0.15"/><rect x="24" y="92" width="630" height="18" rx="4" fill="#58a6ff" fill-opacity="0.85"/><text x="734" y="105" class="t t-value">90%</text><text x="24" y="126" class="t t-card-detail">PyTorch · PyTorch Lightning · Rust (Linear Genetic Programm… · Retrieval-Augmented Generatio… · Knowledge Graphs</text><text x="24" y="142" class="t t-card-detail">(SurrealDB) · Q-Learning</text><text x="24" y="170" class="t t-subhdr">WEB DEVELOPMENT &amp; STATIC SITES</text><rect x="24" y="182" width="700" height="18" rx="4" fill="#3fb950" fill-opacity="0.15"/><rect x="24" y="182" width="595" height="18" rx="4" fill="#3fb950" fill-opacity="0.85"/><text x="734" y="195" class="t t-value">85%</text><text x="24" y="216" class="t t-card-detail">Astro · React · TypeScript · Tailwind CSS · MDX</text><text x="24" y="244" class="t t-subhdr">BACKEND &amp; APIS</text><rect x="24" y="256" width="700" height="18" rx="4" fill="#d29922" fill-opacity="0.15"/><rect x="24" y="256" width="560" height="18" rx="4" fill="#d29922" fill-opacity="0.85"/><text x="734" y="269" class="t t-value">80%</text><text x="24" y="290" class="t t-card-detail">Go · FastAPI · OpenAPI · SurrealDB · Graph-enhanced Retrieval</text><text x="24" y="318" class="t t-subhdr">DEVOPS &amp; AUTOMATION</text><rect x="24" y="330" width="700" height="18" rx="4" fill="#f85149" fill-opacity="0.15"/><rect x="24" y="330" width="525" height="18" rx="4" fill="#f85149" fill-opacity="0.85"/><text x="734" y="343" class="t t-value">75%</text><text x="24" y="364" class="t t-card-detail">GitHub Actions · CI/CD · Chezmoi · Nix · Docker</text><text x="24" y="392" class="t t-subhdr">DESKTOP &amp; CLI APPLICATIONS</text><rect x="24" y="404" width="700" height="18" rx="4" fill="#bc8cff" fill-opacity="0.15"/><rect x="24" y="404" width="489.99999999999994" height="18" rx="4" fill="#bc8cff" fill-opacity="0.85"/><text x="734" y="417" class="t t-value">70%</text><text x="24" y="438" class="t t-card-detail">Rust · Go · JavaFX · Wails · Textual</text><text x="24" y="466" class="t t-subhdr">DOCUMENTATION &amp; PUBLISHING</text><rect x="24" y="478" width="700" height="18" rx="4" fill="#39d2c0" fill-opacity="0.15"/><rect x="24" y="478" width="420" height="18" rx="4" fill="#39d2c0" fill-opacity="0.85"/><text x="734" y="491" class="t t-value">60%</text><text x="24" y="512" class="t t-card-detail">TeX/LaTeX · Pandoc · Markdown · PDF Generation</text></svg>
@@ -1,14 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="808" height="350" viewBox="0 0 808 350"><defs><style>
2
- .t { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; }
3
- .t-h { font-size: 13px; fill: #c9d1d9; letter-spacing: 1.5px; font-weight: 600; }
4
- .t-sub { font-size: 11px; fill: #6e7681; }
5
- .t-label { font-size: 12px; fill: #8b949e; }
6
- .t-value { font-size: 11px; fill: #6e7681; }
7
- .t-subhdr { font-size: 11px; fill: #8b949e; letter-spacing: 1px; font-weight: 600; }
8
- .t-stat-label { font-size: 10px; fill: #8b949e; font-weight: 600; }
9
- .t-stat-value { font-size: 22px; font-weight: 700; }
10
- .t-card-title { font-size: 12px; fill: #58a6ff; font-weight: 700; }
11
- .t-card-detail { font-size: 11px; fill: #8b949e; }
12
- .t-pill { font-size: 11px; font-weight: 600; }
13
- .t-bullet { font-size: 12px; fill: #c9d1d9; }
14
- </style></defs><rect width="808" height="350" rx="12" fill="#0d1117"/><text x="24" y="40" class="t t-h">LANGUAGES</text><text x="24" y="56" class="t t-sub">By bytes of code across all public repos</text><circle cx="114" cy="156" r="70" fill="none" stroke="#00ADD8" stroke-width="28" stroke-dasharray="120.51149419170444 319.31147731086656" stroke-dashoffset="0" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#3178c6" stroke-width="28" stroke-dasharray="110.8353888186479 328.9875826839231" stroke-dashoffset="-120.51149419170444" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#dea584" stroke-width="28" stroke-dasharray="108.63627396113505 331.186697541436" stroke-dashoffset="-231.34688301035234" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#3D6117" stroke-width="28" stroke-dasharray="19.792033717615695 420.0309377849553" stroke-dashoffset="-339.9831569714874" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#89e051" stroke-width="28" stroke-dasharray="15.833626974092558 423.98934452847845" stroke-dashoffset="-359.7751906891031" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#3572A5" stroke-width="28" stroke-dasharray="13.634512116579701 426.18845938599134" stroke-dashoffset="-375.6088176631957" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#a52a22" stroke-width="28" stroke-dasharray="7.916813487046279 431.90615801552474" stroke-dashoffset="-389.24332977977537" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#b07219" stroke-width="28" stroke-dasharray="6.597344572538565 433.22562693003243" stroke-dashoffset="-397.16014326682165" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#663399" stroke-width="28" stroke-dasharray="5.717698629533424 434.1052728730376" stroke-dashoffset="-403.75748783936024" transform="rotate(-90 114 156)" opacity="0.85"/><circle cx="114" cy="156" r="70" fill="none" stroke="#000080" stroke-width="28" stroke-dasharray="5.717698629533424 434.1052728730376" stroke-dashoffset="-409.47518646889364" transform="rotate(-90 114 156)" opacity="0.85"/><text x="114" y="161" class="t" fill="#c9d1d9" font-size="14" font-weight="700" text-anchor="middle">10</text><text x="114" y="176" class="t" fill="#6e7681" font-size="10" text-anchor="middle">languages</text><rect x="244" y="76" width="12" height="12" rx="2" fill="#00ADD8" opacity="0.85"/><text x="264" y="86" class="t t-label">Go</text><text x="444" y="86" class="t t-value" text-anchor="end">27.4%</text><rect x="244" y="100" width="12" height="12" rx="2" fill="#3178c6" opacity="0.85"/><text x="264" y="110" class="t t-label">TypeScript</text><text x="444" y="110" class="t t-value" text-anchor="end">25.2%</text><rect x="244" y="124" width="12" height="12" rx="2" fill="#dea584" opacity="0.85"/><text x="264" y="134" class="t t-label">Rust</text><text x="444" y="134" class="t t-value" text-anchor="end">24.7%</text><rect x="244" y="148" width="12" height="12" rx="2" fill="#3D6117" opacity="0.85"/><text x="264" y="158" class="t t-label">TeX</text><text x="444" y="158" class="t t-value" text-anchor="end">4.5%</text><rect x="244" y="172" width="12" height="12" rx="2" fill="#89e051" opacity="0.85"/><text x="264" y="182" class="t t-label">Shell</text><text x="444" y="182" class="t t-value" text-anchor="end">3.6%</text><rect x="244" y="196" width="12" height="12" rx="2" fill="#3572A5" opacity="0.85"/><text x="264" y="206" class="t t-label">Python</text><text x="444" y="206" class="t t-value" text-anchor="end">3.1%</text><rect x="244" y="220" width="12" height="12" rx="2" fill="#a52a22" opacity="0.85"/><text x="264" y="230" class="t t-label">Jinja</text><text x="444" y="230" class="t t-value" text-anchor="end">1.8%</text><rect x="244" y="244" width="12" height="12" rx="2" fill="#b07219" opacity="0.85"/><text x="264" y="254" class="t t-label">Java</text><text x="444" y="254" class="t t-value" text-anchor="end">1.5%</text><rect x="244" y="268" width="12" height="12" rx="2" fill="#663399" opacity="0.85"/><text x="264" y="278" class="t t-label">CSS</text><text x="444" y="278" class="t t-value" text-anchor="end">1.3%</text><rect x="244" y="292" width="12" height="12" rx="2" fill="#000080" opacity="0.85"/><text x="264" y="302" class="t t-label">Lua</text><text x="444" y="302" class="t t-value" text-anchor="end">1.3%</text></svg>
@@ -1,14 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="808" height="162" viewBox="0 0 808 162"><defs><style>
2
- .t { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif; }
3
- .t-h { font-size: 13px; fill: #c9d1d9; letter-spacing: 1.5px; font-weight: 600; }
4
- .t-sub { font-size: 11px; fill: #6e7681; }
5
- .t-label { font-size: 12px; fill: #8b949e; }
6
- .t-value { font-size: 11px; fill: #6e7681; }
7
- .t-subhdr { font-size: 11px; fill: #8b949e; letter-spacing: 1px; font-weight: 600; }
8
- .t-stat-label { font-size: 10px; fill: #8b949e; font-weight: 600; }
9
- .t-stat-value { font-size: 22px; font-weight: 700; }
10
- .t-card-title { font-size: 12px; fill: #58a6ff; font-weight: 700; }
11
- .t-card-detail { font-size: 11px; fill: #8b949e; }
12
- .t-pill { font-size: 11px; font-weight: 600; }
13
- .t-bullet { font-size: 12px; fill: #c9d1d9; }
14
- </style></defs><rect width="808" height="162" rx="12" fill="#0d1117"/><text x="24" y="40" class="t t-h">AT A GLANCE</text><text x="24" y="56" class="t t-sub">Contribution activity over the past year</text><rect x="24" y="66" width="140" height="72" rx="8" fill="#161b22" stroke="#30363d" stroke-width="1"/><circle cx="38" cy="82" r="4" fill="#58a6ff"/><text x="48" y="86" class="t t-stat-label">COMMITS</text><text x="94" y="118" fill="#58a6ff" class="t t-stat-value" text-anchor="middle">1,117</text><rect x="179" y="66" width="140" height="72" rx="8" fill="#161b22" stroke="#30363d" stroke-width="1"/><circle cx="193" cy="82" r="4" fill="#3fb950"/><text x="203" y="86" class="t t-stat-label">PRS</text><text x="249" y="118" fill="#3fb950" class="t t-stat-value" text-anchor="middle">3</text><rect x="334" y="66" width="140" height="72" rx="8" fill="#161b22" stroke="#30363d" stroke-width="1"/><circle cx="348" cy="82" r="4" fill="#d29922"/><text x="358" y="86" class="t t-stat-label">REVIEWS</text><text x="404" y="118" fill="#d29922" class="t t-stat-value" text-anchor="middle">5</text><rect x="489" y="66" width="140" height="72" rx="8" fill="#161b22" stroke="#30363d" stroke-width="1"/><circle cx="503" cy="82" r="4" fill="#bc8cff"/><text x="513" y="86" class="t t-stat-label">REPOS</text><text x="559" y="118" fill="#bc8cff" class="t t-stat-value" text-anchor="middle">26</text></svg>
@@ -1,38 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { Fragment, h } from "../jsx-factory.js";
3
- import type { BarItem } from "../types.js";
4
- import { renderBarChart } from "./bar-chart.js";
5
-
6
- void Fragment;
7
-
8
- describe("renderBarChart", () => {
9
- const items: BarItem[] = [
10
- { name: "TypeScript", value: 75, percent: "75.0", color: "#3178c6" },
11
- { name: "JavaScript", value: 25, percent: "25.0", color: "#f1e05a" },
12
- ];
13
-
14
- it("returns { svg, height }", () => {
15
- const result = renderBarChart(items, 0);
16
- expect(result).toHaveProperty("svg");
17
- expect(result).toHaveProperty("height");
18
- expect(typeof result.svg).toBe("string");
19
- expect(typeof result.height).toBe("number");
20
- });
21
-
22
- it("height matches item count * row height", () => {
23
- const result = renderBarChart(items, 0);
24
- expect(result.height).toBe(items.length * 48);
25
- });
26
-
27
- it("svg contains item names", () => {
28
- const result = renderBarChart(items, 0);
29
- expect(result.svg).toContain("TypeScript");
30
- expect(result.svg).toContain("JavaScript");
31
- });
32
-
33
- it("returns empty for empty input", () => {
34
- const result = renderBarChart([], 0);
35
- expect(result.svg).toBe("");
36
- expect(result.height).toBe(0);
37
- });
38
- });
@@ -1,54 +0,0 @@
1
- import { Fragment, h } from "../jsx-factory.js";
2
- import { escapeXml, truncate } from "../svg-utils.js";
3
- import { BAR_COLORS, LAYOUT } from "../theme.js";
4
- import type { BarItem, RenderResult } from "../types.js";
5
-
6
- export function renderBarChart(
7
- items: BarItem[],
8
- y: number,
9
- options: Record<string, unknown> = {},
10
- ): RenderResult {
11
- if (items.length === 0) return { svg: "", height: 0 };
12
-
13
- const { barHeight, barRowHeight, barMaxWidth, padX } = LAYOUT;
14
- const useItemColors = options.useItemColors === true;
15
- const maxValue = Math.max(...items.map((d) => d.value));
16
-
17
- const svg = (
18
- <>
19
- {items.map((item, i) => {
20
- const ry = y + i * barRowHeight;
21
- const barWidth = Math.max((item.value / maxValue) * barMaxWidth, 4);
22
- const color = useItemColors
23
- ? item.color || BAR_COLORS[i % BAR_COLORS.length]
24
- : BAR_COLORS[i % BAR_COLORS.length];
25
- const label = escapeXml(truncate(item.name, 40));
26
- const valueLabel = item.percent
27
- ? `${item.percent}%`
28
- : String(item.value);
29
-
30
- return (
31
- <>
32
- <text x={padX} y={ry + 14} className="t t-label">
33
- {label}
34
- </text>
35
- <rect
36
- x={padX}
37
- y={ry + 26}
38
- width={barWidth}
39
- height={barHeight}
40
- rx="3"
41
- fill={color}
42
- opacity="0.85"
43
- />
44
- <text x={padX + barWidth + 8} y={ry + 40} className="t t-value">
45
- {valueLabel}
46
- </text>
47
- </>
48
- );
49
- })}
50
- </>
51
- );
52
-
53
- return { svg, height: items.length * barRowHeight };
54
- }
@@ -1,44 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { makeContributionCalendar } from "../__fixtures__/repos.js";
3
- import { Fragment, h } from "../jsx-factory.js";
4
- import { renderContributionCalendar } from "./contribution-calendar.js";
5
-
6
- void Fragment;
7
-
8
- describe("renderContributionCalendar", () => {
9
- const calendar = makeContributionCalendar();
10
-
11
- it("returns { svg, height }", () => {
12
- const result = renderContributionCalendar(calendar, 0);
13
- expect(result).toHaveProperty("svg");
14
- expect(result).toHaveProperty("height");
15
- expect(typeof result.svg).toBe("string");
16
- expect(result.height).toBeGreaterThan(0);
17
- });
18
-
19
- it("renders rect cells for each contribution day", () => {
20
- const { svg } = renderContributionCalendar(calendar, 0);
21
- // 2 weeks x 7 days = 14 rects
22
- const rectCount = (svg.match(/<rect /g) || []).length;
23
- expect(rectCount).toBe(14);
24
- });
25
-
26
- it("includes month labels", () => {
27
- const { svg } = renderContributionCalendar(calendar, 0);
28
- expect(svg).toContain("Jan");
29
- });
30
-
31
- it("includes day labels", () => {
32
- const { svg } = renderContributionCalendar(calendar, 0);
33
- expect(svg).toContain("Mon");
34
- expect(svg).toContain("Wed");
35
- expect(svg).toContain("Fri");
36
- });
37
-
38
- it("uses colors from the calendar data", () => {
39
- const { svg } = renderContributionCalendar(calendar, 0);
40
- expect(svg).toContain("#0e4429");
41
- expect(svg).toContain("#006d32");
42
- expect(svg).toContain("#39d353");
43
- });
44
- });
@@ -1,94 +0,0 @@
1
- import { Fragment, h } from "../jsx-factory.js";
2
- import { escapeXml } from "../svg-utils.js";
3
- import { LAYOUT, THEME } from "../theme.js";
4
- import type { ContributionCalendar, RenderResult } from "../types.js";
5
-
6
- const CELL_SIZE = 11;
7
- const CELL_GAP = 2;
8
- const STEP = CELL_SIZE + CELL_GAP;
9
- const DAY_LABEL_WIDTH = 30;
10
- const MONTH_LABEL_HEIGHT = 16;
11
- const DAY_LABELS = ["", "Mon", "", "Wed", "", "Fri", ""];
12
- const MONTH_NAMES = [
13
- "Jan",
14
- "Feb",
15
- "Mar",
16
- "Apr",
17
- "May",
18
- "Jun",
19
- "Jul",
20
- "Aug",
21
- "Sep",
22
- "Oct",
23
- "Nov",
24
- "Dec",
25
- ];
26
-
27
- export function renderContributionCalendar(
28
- calendar: ContributionCalendar,
29
- y: number,
30
- ): RenderResult {
31
- const { padX } = LAYOUT;
32
- const weeks = calendar.weeks;
33
- const gridX = padX + DAY_LABEL_WIDTH;
34
- const gridY = y + MONTH_LABEL_HEIGHT;
35
-
36
- // Build month labels from the first day of each week
37
- const monthLabels: { label: string; x: number }[] = [];
38
- let lastMonth = -1;
39
- for (let w = 0; w < weeks.length; w++) {
40
- const days = weeks[w].contributionDays;
41
- if (days.length === 0) continue;
42
- const month = new Date(days[0].date).getMonth();
43
- if (month !== lastMonth) {
44
- monthLabels.push({
45
- label: MONTH_NAMES[month],
46
- x: gridX + w * STEP,
47
- });
48
- lastMonth = month;
49
- }
50
- }
51
-
52
- const svg = (
53
- <>
54
- {/* Month labels */}
55
- {monthLabels.map((m) => (
56
- <text x={m.x} y={y + 11} className="t t-value">
57
- {escapeXml(m.label)}
58
- </text>
59
- ))}
60
- {/* Day labels */}
61
- {DAY_LABELS.map((label, d) =>
62
- label ? (
63
- <text
64
- x={padX}
65
- y={gridY + d * STEP + CELL_SIZE - 1}
66
- className="t t-value"
67
- >
68
- {escapeXml(label)}
69
- </text>
70
- ) : (
71
- ""
72
- ),
73
- )}
74
- {/* Cells */}
75
- {weeks.map((week, w) =>
76
- week.contributionDays.map((day, d) => (
77
- <rect
78
- x={gridX + w * STEP}
79
- y={gridY + d * STEP}
80
- width={CELL_SIZE}
81
- height={CELL_SIZE}
82
- rx="2"
83
- fill={day.color || THEME.cardBg}
84
- />
85
- )),
86
- )}
87
- </>
88
- );
89
-
90
- const height = MONTH_LABEL_HEIGHT + 7 * STEP;
91
- return { svg, height };
92
- }
93
-
94
- void Fragment;
@@ -1,36 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { Fragment, h } from "../jsx-factory.js";
3
- import type { ContributionHighlight } from "../types.js";
4
- import { renderContributionCards } from "./contribution-cards.js";
5
-
6
- void Fragment;
7
-
8
- describe("renderContributionCards", () => {
9
- const highlights: ContributionHighlight[] = [
10
- { project: "org/repo-one", detail: "★ 500 · TypeScript" },
11
- { project: "org/repo-two", detail: "★ 200 · Go" },
12
- ];
13
-
14
- it("returns { svg, height }", () => {
15
- const result = renderContributionCards(highlights, 0);
16
- expect(result).toHaveProperty("svg");
17
- expect(result).toHaveProperty("height");
18
- });
19
-
20
- it("height accounts for cards and gaps", () => {
21
- const result = renderContributionCards(highlights, 0);
22
- // 2 cards of 44px + 1 gap of 8px = 96
23
- expect(result.height).toBe(2 * 44 + 1 * 8);
24
- });
25
-
26
- it("svg contains project names", () => {
27
- const result = renderContributionCards(highlights, 0);
28
- expect(result.svg).toContain("org/repo-one");
29
- expect(result.svg).toContain("org/repo-two");
30
- });
31
-
32
- it("handles empty input", () => {
33
- const result = renderContributionCards([], 0);
34
- expect(result.height).toBe(0);
35
- });
36
- });
@@ -1,58 +0,0 @@
1
- import { Fragment, h } from "../jsx-factory.js";
2
- import { escapeXml, truncate } from "../svg-utils.js";
3
- import { BAR_COLORS, LAYOUT, THEME } from "../theme.js";
4
- import type { ContributionHighlight, RenderResult } from "../types.js";
5
-
6
- export function renderContributionCards(
7
- highlights: ContributionHighlight[],
8
- y: number,
9
- ): RenderResult {
10
- const { padX } = LAYOUT;
11
- const cardW = 760;
12
- const cardH = 44;
13
- const gap = 8;
14
-
15
- const svg = (
16
- <>
17
- {highlights.map((hl, i) => {
18
- const cy = y + i * (cardH + gap);
19
- const color = BAR_COLORS[i % BAR_COLORS.length];
20
-
21
- return (
22
- <>
23
- <rect
24
- x={padX}
25
- y={cy}
26
- width={cardW}
27
- height={cardH}
28
- rx="6"
29
- fill={THEME.cardBg}
30
- stroke={THEME.border}
31
- stroke-width="1"
32
- />
33
- <rect
34
- x={padX}
35
- y={cy}
36
- width="4"
37
- height={cardH}
38
- rx="2"
39
- fill={color}
40
- />
41
- <text x={padX + 16} y={cy + 18} className="t t-card-title">
42
- {escapeXml(truncate(hl.project, 40))}
43
- </text>
44
- <text x={padX + 16} y={cy + 34} className="t t-card-detail">
45
- {escapeXml(truncate(hl.detail, 80))}
46
- </text>
47
- </>
48
- );
49
- })}
50
- </>
51
- );
52
-
53
- return {
54
- svg,
55
- height:
56
- highlights.length * (cardH + gap) - (highlights.length > 0 ? gap : 0),
57
- };
58
- }
@@ -1,36 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { Fragment, h } from "../jsx-factory.js";
3
- import type { LanguageItem } from "../types.js";
4
- import { renderDonutChart } from "./donut-chart.js";
5
-
6
- void Fragment;
7
-
8
- describe("renderDonutChart", () => {
9
- const items: LanguageItem[] = [
10
- { name: "TypeScript", value: 60000, percent: "60.0", color: "#3178c6" },
11
- { name: "Python", value: 40000, percent: "40.0", color: "#3572A5" },
12
- ];
13
-
14
- it("returns { svg, height }", () => {
15
- const result = renderDonutChart(items, 0);
16
- expect(result).toHaveProperty("svg");
17
- expect(result).toHaveProperty("height");
18
- });
19
-
20
- it("height is at least 180", () => {
21
- const result = renderDonutChart(items, 0);
22
- expect(result.height).toBeGreaterThanOrEqual(180);
23
- });
24
-
25
- it("svg contains language names", () => {
26
- const result = renderDonutChart(items, 0);
27
- expect(result.svg).toContain("TypeScript");
28
- expect(result.svg).toContain("Python");
29
- });
30
-
31
- it("svg contains percentages", () => {
32
- const result = renderDonutChart(items, 0);
33
- expect(result.svg).toContain("60.0%");
34
- expect(result.svg).toContain("40.0%");
35
- });
36
- });