From 9333c28d2ce5451a354949d64d33600d633e3148 Mon Sep 17 00:00:00 2001
From: NickiCloud
Date: Wed, 2 Sep 2026 21:39:39 +0200
Subject: [PATCH] feat: redesign language breakdown with dedicated
languages.svg and shields badges [skip ci]
---
.gitea/workflows/update-profile.yml | 5 +-
README.md | 43 +++----
heatmap.svg | 6 +-
languages.svg | 115 ++++++++++++++++++
productivity.svg | 8 +-
src/ProfileManager/Program.cs | 13 +-
src/ProfileManager/Services/ReadmeInjector.cs | 29 ++++-
src/ProfileManager/Services/SvgGenerator.cs | 115 ++++++++++++++++++
8 files changed, 295 insertions(+), 39 deletions(-)
create mode 100644 languages.svg
diff --git a/.gitea/workflows/update-profile.yml b/.gitea/workflows/update-profile.yml
index ef63985..e457486 100644
--- a/.gitea/workflows/update-profile.yml
+++ b/.gitea/workflows/update-profile.yml
@@ -14,6 +14,7 @@ on:
- 'heatmap.svg'
- 'team-stats.svg'
- 'productivity.svg'
+ - 'languages.svg'
- 'README.md'
jobs:
@@ -51,9 +52,9 @@ jobs:
git config --global user.email "bot@webcore-consulting.dev"
# PrΓΌfen, ob sich SVG-Dateien oder README.md geΓ€ndert haben
- if git status --porcelain | grep -E "(heatmap.svg|team-stats.svg|productivity.svg|README.md)"; then
+ if git status --porcelain | grep -E "(heatmap.svg|team-stats.svg|productivity.svg|languages.svg|README.md)"; then
echo "Γnderungen an Grafiken oder README.md erkannt. Committe..."
- git add heatmap.svg team-stats.svg productivity.svg README.md
+ git add heatmap.svg team-stats.svg productivity.svg languages.svg README.md
# GANZ WICHTIG: [skip ci] verhindert Endlosschleifen in Gitea Actions!
git commit -m "chore(profile): update personal & team telemetry [skip ci]"
diff --git a/README.md b/README.md
index e6877fb..e5b5b23 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -78,15 +78,20 @@ Ich bin **Nicki (Nicklas)**, angehender Softwareentwickler mit fundiertem Fokus
#### π Byte-genaue Sprachverteilung (Team-Codebase):
+
+
+

+
+
-| Sprache | Anteil | Code-Volumen | Verteilung |
-| :--- | :---: | :---: | :--- |
-| `C#` | **56,2%** | 1,1 MB | `ββββββββββββββββββββ` |
-| `HTML` | **40,4%** | 797,4 KB | `ββββββββββββββββββββ` |
-| `CSS` | **2,3%** | 45,7 KB | `ββββββββββββββββββββ` |
-| `JavaScript` | **0,4%** | 8,6 KB | `ββββββββββββββββββββ` |
-| `PowerShell` | **0,4%** | 7,6 KB | `ββββββββββββββββββββ` |
-| `Shell` | **0,2%** | 3,4 KB | `ββββββββββββββββββββ` |
+| Sprache | Technologie | Anteil | Code-Volumen | Relative Verteilung |
+| :--- | :---: | :---: | :---: | :--- |
+| **C#** |  | **56,2%** | `1,1 MB` | `β°β°β°β°β°β°β°β°β°β°β°β±β±β±β±β±β±β±β±β±` |
+| **HTML** |  | **40,4%** | `797,4 KB` | `β°β°β°β°β°β°β°β°β±β±β±β±β±β±β±β±β±β±β±β±` |
+| **CSS** |  | **2,3%** | `45,7 KB` | `β°β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±` |
+| **JavaScript** |  | **0,4%** | `8,6 KB` | `β°β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±` |
+| **PowerShell** |  | **0,4%** | `7,6 KB` | `β°β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±` |
+| **Shell** |  | **0,2%** | `3,4 KB` | `β°β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±β±` |
#### π Aktive Team-Repositories:
@@ -109,11 +114,11 @@ Ich bin **Nicki (Nicklas)**, angehender Softwareentwickler mit fundiertem Fokus
> *Echtzeit-Feed der letzten Aktionen und Commits ΓΌber alle privaten & Team-Repositories.*
-- β‘ **feat: add productivity biorhythm clock, developer achievements and quote [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 15 Minuten*
-- β‘ **feat: add live commit radar, activity feed and homelab telemetry [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 18 Minuten*
-- β‘ **feat: add team telemetry dashboard, byte-level language analytics and tabs [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 22 Minuten*
-- β‘ **fix(svg): remove utf-8 bom and add xml declaration for gitea typesniffer [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 30 Minuten*
-- β‘ **chore(profile): update activity heatmap & stats (22 contributions) [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 37 Minuten*
+- β‘ **fix(svg): escape xml characters in productivity badge [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 6 Minuten*
+- β‘ **feat: add productivity biorhythm clock, developer achievements and quote [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 22 Minuten*
+- β‘ **feat: add live commit radar, activity feed and homelab telemetry [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 26 Minuten*
+- β‘ **feat: add team telemetry dashboard, byte-level language analytics and tabs [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 29 Minuten*
+- β‘ **fix(svg): remove utf-8 bom and add xml declaration for gitea typesniffer [skip ci]** auf [`NickiCloud/.profile`](https://git.webcore-consulting.dev/NickiCloud/.profile) β’ π *vor 37 Minuten*
@@ -129,16 +134,6 @@ Ich bin **Nicki (Nicklas)**, angehender Softwareentwickler mit fundiertem Fokus
-#### π Entwickler-Erfolge & Achievements:
-
-| Trophy | Rang | Beschreibung | Status |
-| :--- | :---: | :--- | :---: |
-| π **Streak Master** | `Gold` | Mindestens 4 Tage ununterbrochen aktiv gecodet | **4 Tage** β
|
-| πΎ **Code Sovereign** | `Diamond` | Γber 1,5 MB Code in Gitea verwaltet | **1,9 MB** β
|
-| β‘ **Zero-Dependency** | `Master` | Reines .NET 10 BCL ohne fremde NuGet-Pakete | **100% Native** β
|
-| π **Night-Owl Veteran** | `Elite` | Hohe nΓ€chtliche Coding-AktivitΓ€t | **21,1% Nachts** β
|
-| π° **Homelab Overlord** | `Legendary` | Self-Hosted Debian, Docker, Gitea, Mailcow | **Vollautonom** β
|
-
#### π‘ TΓ€gliche Entwickler-Philosophie:
diff --git a/heatmap.svg b/heatmap.svg
index 3487580..693c7ef 100644
--- a/heatmap.svg
+++ b/heatmap.svg
@@ -74,7 +74,7 @@
π¦ NickiCloud β’ PersΓΆnliche AktivitΓ€t
- 27 Commits im letzten Jahr
+ 28 Commits im letzten Jahr
Sep
@@ -1173,7 +1173,7 @@
01.09.2026: 9 Commits
- 02.09.2026: 11 Commits
+ 02.09.2026: 12 Commits
03.09.2026: 0 Commits
@@ -1192,7 +1192,7 @@
π¦
- 27
+ 28
Jahres-BeitrΓ€ge
diff --git a/languages.svg b/languages.svg
new file mode 100644
index 0000000..40b3c83
--- /dev/null
+++ b/languages.svg
@@ -0,0 +1,115 @@
+
+
diff --git a/productivity.svg b/productivity.svg
index dc2c1ea..0773f91 100644
--- a/productivity.svg
+++ b/productivity.svg
@@ -69,15 +69,15 @@
π Abend
(18:00 - 24:00)
-
- 73,7%
+
+ 78,9%
π Nacht
(00:00 - 06:00)
-
- 21,1%
+
+ 15,8%
diff --git a/src/ProfileManager/Program.cs b/src/ProfileManager/Program.cs
index f4c341d..6af6e18 100644
--- a/src/ProfileManager/Program.cs
+++ b/src/ProfileManager/Program.cs
@@ -23,12 +23,14 @@ internal static class Program
var svgPath = GetConfigValue(args, "--output-svg", "OUTPUT_SVG", "heatmap.svg");
var teamSvgPath = GetConfigValue(args, "--team-svg", "TEAM_SVG", "team-stats.svg");
var prodSvgPath = GetConfigValue(args, "--prod-svg", "PROD_SVG", "productivity.svg");
+ var langSvgPath = GetConfigValue(args, "--lang-svg", "LANG_SVG", "languages.svg");
var readmePath = GetConfigValue(args, "--readme", "README_PATH", "README.md");
var repoRoot = FindRepoRoot();
if (!Path.IsPathRooted(svgPath)) svgPath = Path.GetFullPath(Path.Combine(repoRoot, svgPath));
if (!Path.IsPathRooted(teamSvgPath)) teamSvgPath = Path.GetFullPath(Path.Combine(repoRoot, teamSvgPath));
if (!Path.IsPathRooted(prodSvgPath)) prodSvgPath = Path.GetFullPath(Path.Combine(repoRoot, prodSvgPath));
+ if (!Path.IsPathRooted(langSvgPath)) langSvgPath = Path.GetFullPath(Path.Combine(repoRoot, langSvgPath));
if (!Path.IsPathRooted(readmePath)) readmePath = Path.GetFullPath(Path.Combine(repoRoot, readmePath));
Console.WriteLine($"[Config] Target User : {username}");
@@ -36,6 +38,7 @@ internal static class Program
Console.WriteLine($"[Config] Heatmap SVG : {svgPath}");
Console.WriteLine($"[Config] Team Stats SVG : {teamSvgPath}");
Console.WriteLine($"[Config] Productivity SVG : {prodSvgPath}");
+ Console.WriteLine($"[Config] Languages SVG : {langSvgPath}");
Console.WriteLine($"[Config] Target README : {readmePath}");
Console.WriteLine();
@@ -105,7 +108,15 @@ internal static class Program
EnsureDirectory(prodSvgPath);
await File.WriteAllTextAsync(prodSvgPath, prodSvg, utf8WithoutBom);
- // Step 7: Inject all sections into README.md
+ // Step 7: Generate Languages SVG
+ Console.WriteLine($"[SVG] Rendering Languages SVG: {langSvgPath}");
+ var teamLangs = stats.TeamStats?.Languages ?? stats.OverallLanguages;
+ long totalCodeBytes = stats.TeamStats?.TotalCodeBytes ?? teamLangs.Sum(l => l.Bytes);
+ var langSvg = SvgGenerator.GenerateLanguages(teamLangs, totalCodeBytes);
+ EnsureDirectory(langSvgPath);
+ await File.WriteAllTextAsync(langSvgPath, langSvg, utf8WithoutBom);
+
+ // Step 8: Inject all sections into README.md
if (File.Exists(readmePath))
{
Console.WriteLine($"[README] Injecting personal, team & language metrics into: {readmePath}");
diff --git a/src/ProfileManager/Services/ReadmeInjector.cs b/src/ProfileManager/Services/ReadmeInjector.cs
index 778e7f8..de1761f 100644
--- a/src/ProfileManager/Services/ReadmeInjector.cs
+++ b/src/ProfileManager/Services/ReadmeInjector.cs
@@ -96,14 +96,19 @@ public static class ReadmeInjector
content = ReplaceSection(content, StartLangMarker, EndLangMarker, sb =>
{
var langs = stats.TeamStats?.Languages ?? stats.OverallLanguages;
- sb.AppendLine("| Sprache | Anteil | Code-Volumen | Verteilung |");
- sb.AppendLine("| :--- | :---: | :---: | :--- |");
+ sb.AppendLine("| Sprache | Technologie | Anteil | Code-Volumen | Relative Verteilung |");
+ sb.AppendLine("| :--- | :---: | :---: | :---: | :--- |");
foreach (var l in langs.Take(6))
{
- int filledBlocks = (int)Math.Round(l.Percentage / 5.0);
- string bar = new string('β', filledBlocks).PadRight(20, 'β');
- sb.AppendLine($"| `{l.Name}` | **{l.Percentage:F1}%** | {l.FormattedSize} | `{bar}` |");
+ var (logo, logoColor) = GetLanguageBadgeInfo(l.Name);
+ var colorHex = l.Color.TrimStart('#');
+ var badgeUrl = $"https://img.shields.io/badge/{Uri.EscapeDataString(l.Name)}-{colorHex}?style=flat-square&logo={logo}&logoColor={logoColor}";
+
+ int filledBlocks = Math.Max(1, (int)Math.Round(l.Percentage / 5.0));
+ string bar = new string('β°', filledBlocks).PadRight(20, 'β±');
+
+ sb.AppendLine($"| **{l.Name}** |  | **{l.Percentage:F1}%** | `{l.FormattedSize}` | `{bar}` |");
}
});
@@ -210,4 +215,18 @@ public static class ReadmeInjector
if (diff.TotalDays < 30) return $"vor {(int)diff.TotalDays} Tagen";
return dateTime.ToString("dd.MM.yyyy");
}
+
+ private static (string Logo, string LogoColor) GetLanguageBadgeInfo(string lang) => lang.ToLowerInvariant() switch
+ {
+ "c#" => ("csharp", "white"),
+ "html" => ("html5", "white"),
+ "css" => ("css3", "white"),
+ "javascript" => ("javascript", "black"),
+ "typescript" => ("typescript", "white"),
+ "powershell" => ("powershell", "white"),
+ "shell" or "bash" => ("gnubash", "white"),
+ "dockerfile" => ("docker", "white"),
+ "python" => ("python", "white"),
+ _ => ("code", "white")
+ };
}
diff --git a/src/ProfileManager/Services/SvgGenerator.cs b/src/ProfileManager/Services/SvgGenerator.cs
index db206a6..1a197cd 100644
--- a/src/ProfileManager/Services/SvgGenerator.cs
+++ b/src/ProfileManager/Services/SvgGenerator.cs
@@ -429,6 +429,121 @@ public static class SvgGenerator
return sb.ToString();
}
+ public static string GenerateLanguages(List languages, long totalBytes)
+ {
+ var sb = new StringBuilder(8192);
+
+ const int svgWidth = 740;
+ const int svgHeight = 150;
+ const int barStartX = 34;
+ const double barWidth = 672.0;
+
+ string formattedTotal = totalBytes switch
+ {
+ >= 1024 * 1024 => $"{(double)totalBytes / (1024 * 1024):F1} MB",
+ >= 1024 => $"{(double)totalBytes / 1024:F1} KB",
+ _ => $"{totalBytes} B"
+ };
+
+ sb.AppendLine("""""");
+ sb.AppendLine($"""");
+ return sb.ToString();
+ }
+
private static string XmlEscape(string? text)
{
if (string.IsNullOrEmpty(text)) return string.Empty;