feat: redesign language breakdown with dedicated languages.svg and shields badges [skip ci]
This commit is contained in:
parent
e7ffe773d7
commit
9333c28d2c
@ -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]"
|
||||
|
||||
43
README.md
43
README.md
@ -12,7 +12,7 @@
|
||||
<img src="https://img.shields.io/badge/Gitea_Server-Online_%E2%97%8F-2ea44f?style=flat-square&logo=gitea&logoColor=white" alt="Gitea Online" />
|
||||
<img src="https://img.shields.io/badge/Homelab-Debian_12_Bookworm-A81D33?style=flat-square&logo=debian&logoColor=white" alt="Debian 12" />
|
||||
<img src="https://img.shields.io/badge/CI%2FCD-Gitea_Act_Runner-609926?style=flat-square&logo=docker&logoColor=white" alt="Runner Active" />
|
||||
<img src="https://img.shields.io/badge/Telemetry-Zuletzt_aktualisiert_02.09.2026%2019%3A31_UTC-7e3f98?style=flat-square" alt="Last Sync" />
|
||||
<img src="https://img.shields.io/badge/Telemetry-Zuletzt_aktualisiert_02.09.2026%2019%3A38_UTC-7e3f98?style=flat-square" alt="Last Sync" />
|
||||
</p>
|
||||
<!-- END_SYS_TELEMETRY -->
|
||||
|
||||
@ -78,15 +78,20 @@ Ich bin **Nicki (Nicklas)**, angehender Softwareentwickler mit fundiertem Fokus
|
||||
</div>
|
||||
|
||||
#### 📈 Byte-genaue Sprachverteilung (Team-Codebase):
|
||||
|
||||
<div align="center">
|
||||
<img src="languages.svg" alt="Byte-genaue Sprachverteilung" width="100%" />
|
||||
</div>
|
||||
|
||||
<!-- START_LANGUAGE_METRICS -->
|
||||
| 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` | `▰▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱` |
|
||||
<!-- END_LANGUAGE_METRICS -->
|
||||
|
||||
#### 🚀 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.*
|
||||
|
||||
<!-- START_RECENT_ACTIVITY -->
|
||||
- ⚡ **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*
|
||||
<!-- END_RECENT_ACTIVITY -->
|
||||
|
||||
</details>
|
||||
@ -129,16 +134,6 @@ Ich bin **Nicki (Nicklas)**, angehender Softwareentwickler mit fundiertem Fokus
|
||||
<img src="productivity.svg" alt="Nicki's Coding Productivity Clock" width="100%" />
|
||||
</div>
|
||||
|
||||
#### 🏆 Entwickler-Erfolge & Achievements:
|
||||
<!-- START_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** ✅ |
|
||||
<!-- END_ACHIEVEMENTS -->
|
||||
|
||||
#### 💡 Tägliche Entwickler-Philosophie:
|
||||
<!-- START_RANDOM_QUOTE -->
|
||||
|
||||
@ -74,7 +74,7 @@
|
||||
<text class="title" x="0" y="14">🦊 NickiCloud <tspan font-weight="400" fill="var(--accent)">•</tspan> <tspan font-weight="500" font-size="12" fill="var(--text-sub)">Persönliche Aktivität</tspan></text>
|
||||
<rect x="510" y="0" width="190" height="22" rx="11" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="1" />
|
||||
<circle cx="522" cy="11" r="4" fill="var(--accent)" />
|
||||
<text class="subtitle" x="532" y="15" font-weight="600">27 Commits im letzten Jahr</text>
|
||||
<text class="subtitle" x="532" y="15" font-weight="600">28 Commits im letzten Jahr</text>
|
||||
</g>
|
||||
<!-- Month Labels -->
|
||||
<text class="lbl-month" x="34" y="54">Sep</text>
|
||||
@ -1173,7 +1173,7 @@
|
||||
<title>01.09.2026: 9 Commits</title>
|
||||
</rect>
|
||||
<rect class="cell lvl-4" x="697" y="90" width="10" height="10" rx="2" style="animation-delay: 612ms;">
|
||||
<title>02.09.2026: 11 Commits</title>
|
||||
<title>02.09.2026: 12 Commits</title>
|
||||
</rect>
|
||||
<rect class="cell lvl-0" x="697" y="103" width="10" height="10" rx="2" style="animation-delay: 612ms;">
|
||||
<title>03.09.2026: 0 Commits</title>
|
||||
@ -1192,7 +1192,7 @@
|
||||
<g transform="translate(34, 166)">
|
||||
<rect width="216" height="36" rx="8" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="1" />
|
||||
<text x="12" y="27" font-size="16">📦</text>
|
||||
<text class="stat-val" x="40" y="20">27</text>
|
||||
<text class="stat-val" x="40" y="20">28</text>
|
||||
<text class="stat-lbl" x="40" y="33">Jahres-Beiträge</text>
|
||||
</g>
|
||||
<g transform="translate(264, 166)">
|
||||
|
||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
115
languages.svg
Normal file
115
languages.svg
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 740 150" width="100%" height="100%">
|
||||
<defs>
|
||||
<clipPath id="mainLangClip">
|
||||
<rect x="34" y="50" width="672" height="14" rx="7" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #11111b;
|
||||
--card: #181825;
|
||||
--border: #313244;
|
||||
--text-main: #cdd6f4;
|
||||
--text-sub: #a6adc8;
|
||||
--text-muted: #6c7086;
|
||||
--accent: #d980fa;
|
||||
--metric-bg: #1e1e2e;
|
||||
--metric-border: #313244;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--bg: #f8fafc;
|
||||
--card: #ffffff;
|
||||
--border: #e2e8f0;
|
||||
--text-main: #0f172a;
|
||||
--text-sub: #334155;
|
||||
--text-muted: #64748b;
|
||||
--accent: #7e22ce;
|
||||
--metric-bg: #f1f5f9;
|
||||
--metric-border: #cbd5e1;
|
||||
}
|
||||
}
|
||||
.bg-panel { fill: var(--bg); stroke: var(--border); stroke-width: 1.2px; rx: 12px; }
|
||||
.title { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 13px; font-weight: 700; fill: var(--text-main); }
|
||||
.subtitle { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 11px; fill: var(--text-sub); }
|
||||
.lang-name { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 11px; font-weight: 600; fill: var(--text-main); }
|
||||
.lang-pct { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 11px; font-weight: 700; fill: var(--accent); }
|
||||
.lang-size { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 10px; fill: var(--text-muted); }
|
||||
</style>
|
||||
<rect class="bg-panel" x="1" y="1" width="738" height="148" />
|
||||
<g transform="translate(24, 18)">
|
||||
<text class="title" x="0" y="14">📈 Byte-genaue Sprachverteilung <tspan font-weight="400" fill="var(--accent)">•</tspan> <tspan font-weight="500" font-size="11" fill="var(--text-sub)">Team-Codebase</tspan></text>
|
||||
<rect x="445" y="0" width="255" height="22" rx="11" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="1" />
|
||||
<circle cx="457" cy="11" r="4" fill="var(--accent)" />
|
||||
<text class="subtitle" x="467" y="15" font-weight="600">Gesamt: 1,9 MB analysierter Code</text>
|
||||
</g>
|
||||
<g clip-path="url(#mainLangClip)">
|
||||
<rect x="34.0" y="50" width="377.7" height="14" fill="#a855f7">
|
||||
<title>C#: 56,2% (1,1 MB)</title>
|
||||
</rect>
|
||||
<rect x="411.7" y="50" width="271.5" height="14" fill="#e34c26">
|
||||
<title>HTML: 40,4% (797,4 KB)</title>
|
||||
</rect>
|
||||
<rect x="683.2" y="50" width="15.5" height="14" fill="#563d7c">
|
||||
<title>CSS: 2,3% (45,7 KB)</title>
|
||||
</rect>
|
||||
<rect x="698.6" y="50" width="2.7" height="14" fill="#f1e05a">
|
||||
<title>JavaScript: 0,4% (8,6 KB)</title>
|
||||
</rect>
|
||||
<rect x="701.3" y="50" width="2.7" height="14" fill="#012456">
|
||||
<title>PowerShell: 0,4% (7,6 KB)</title>
|
||||
</rect>
|
||||
<rect x="704.0" y="50" width="2.5" height="14" fill="#89e051">
|
||||
<title>Shell: 0,2% (3,4 KB)</title>
|
||||
</rect>
|
||||
<rect x="706.5" y="50" width="2.5" height="14" fill="#384d54">
|
||||
<title>Dockerfile: 0,1% (2,4 KB)</title>
|
||||
</rect>
|
||||
</g>
|
||||
<!-- Language Legend Matrix -->
|
||||
<g transform="translate(34, 84)">
|
||||
<g transform="translate(0, 0)">
|
||||
<rect x="0" y="0" width="212" height="22" rx="6" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<circle cx="12" cy="11" r="4.5" fill="#a855f7" />
|
||||
<text class="lang-name" x="24" y="15">C#</text>
|
||||
<text class="lang-pct" x="110" y="15">56,2%</text>
|
||||
<text class="lang-size" x="155" y="15">(1,1 MB)</text>
|
||||
</g>
|
||||
<g transform="translate(224, 0)">
|
||||
<rect x="0" y="0" width="212" height="22" rx="6" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<circle cx="12" cy="11" r="4.5" fill="#e34c26" />
|
||||
<text class="lang-name" x="24" y="15">HTML</text>
|
||||
<text class="lang-pct" x="110" y="15">40,4%</text>
|
||||
<text class="lang-size" x="155" y="15">(797,4 KB)</text>
|
||||
</g>
|
||||
<g transform="translate(448, 0)">
|
||||
<rect x="0" y="0" width="212" height="22" rx="6" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<circle cx="12" cy="11" r="4.5" fill="#563d7c" />
|
||||
<text class="lang-name" x="24" y="15">CSS</text>
|
||||
<text class="lang-pct" x="110" y="15">2,3%</text>
|
||||
<text class="lang-size" x="155" y="15">(45,7 KB)</text>
|
||||
</g>
|
||||
<g transform="translate(0, 26)">
|
||||
<rect x="0" y="0" width="212" height="22" rx="6" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<circle cx="12" cy="11" r="4.5" fill="#f1e05a" />
|
||||
<text class="lang-name" x="24" y="15">JavaScript</text>
|
||||
<text class="lang-pct" x="110" y="15">0,4%</text>
|
||||
<text class="lang-size" x="155" y="15">(8,6 KB)</text>
|
||||
</g>
|
||||
<g transform="translate(224, 26)">
|
||||
<rect x="0" y="0" width="212" height="22" rx="6" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<circle cx="12" cy="11" r="4.5" fill="#012456" />
|
||||
<text class="lang-name" x="24" y="15">PowerShell</text>
|
||||
<text class="lang-pct" x="110" y="15">0,4%</text>
|
||||
<text class="lang-size" x="155" y="15">(7,6 KB)</text>
|
||||
</g>
|
||||
<g transform="translate(448, 26)">
|
||||
<rect x="0" y="0" width="212" height="22" rx="6" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<circle cx="12" cy="11" r="4.5" fill="#89e051" />
|
||||
<text class="lang-name" x="24" y="15">Shell</text>
|
||||
<text class="lang-pct" x="110" y="15">0,2%</text>
|
||||
<text class="lang-size" x="155" y="15">(3,4 KB)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
@ -69,15 +69,15 @@
|
||||
<text class="bar-lbl" x="0" y="12">🌆 Abend</text>
|
||||
<text class="bar-time" x="110" y="12">(18:00 - 24:00)</text>
|
||||
<rect x="200" y="0" width="430" height="16" rx="4" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<rect x="200" y="0" width="316.9" height="16" rx="4" fill="var(--bar-evening)" />
|
||||
<text class="stat-val" x="644" y="13" font-size="12">73,7%</text>
|
||||
<rect x="200" y="0" width="339.3" height="16" rx="4" fill="var(--bar-evening)" />
|
||||
<text class="stat-val" x="644" y="13" font-size="12">78,9%</text>
|
||||
</g>
|
||||
<!-- Bucket: Nacht -->
|
||||
<g transform="translate(34, 168)">
|
||||
<text class="bar-lbl" x="0" y="12">🌙 Nacht</text>
|
||||
<text class="bar-time" x="110" y="12">(00:00 - 06:00)</text>
|
||||
<rect x="200" y="0" width="430" height="16" rx="4" fill="var(--metric-bg)" stroke="var(--metric-border)" stroke-width="0.8" />
|
||||
<rect x="200" y="0" width="90.7" height="16" rx="4" fill="var(--bar-night)" />
|
||||
<text class="stat-val" x="644" y="13" font-size="12">21,1%</text>
|
||||
<rect x="200" y="0" width="67.9" height="16" rx="4" fill="var(--bar-night)" />
|
||||
<text class="stat-val" x="644" y="13" font-size="12">15,8%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
@ -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}");
|
||||
|
||||
@ -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")
|
||||
};
|
||||
}
|
||||
|
||||
@ -429,6 +429,121 @@ public static class SvgGenerator
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GenerateLanguages(List<LanguageStat> 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("""<?xml version="1.0" encoding="utf-8"?>""");
|
||||
sb.AppendLine($"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {svgWidth} {svgHeight}" width="100%" height="100%">""");
|
||||
sb.AppendLine(" <defs>");
|
||||
sb.AppendLine(" <clipPath id=\"mainLangClip\">");
|
||||
sb.AppendLine($" <rect x=\"{barStartX}\" y=\"50\" width=\"{barWidth}\" height=\"14\" rx=\"7\" />");
|
||||
sb.AppendLine(" </clipPath>");
|
||||
sb.AppendLine(" </defs>");
|
||||
|
||||
sb.AppendLine("""
|
||||
<style>
|
||||
:root {
|
||||
--bg: #11111b;
|
||||
--card: #181825;
|
||||
--border: #313244;
|
||||
--text-main: #cdd6f4;
|
||||
--text-sub: #a6adc8;
|
||||
--text-muted: #6c7086;
|
||||
--accent: #d980fa;
|
||||
--metric-bg: #1e1e2e;
|
||||
--metric-border: #313244;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--bg: #f8fafc;
|
||||
--card: #ffffff;
|
||||
--border: #e2e8f0;
|
||||
--text-main: #0f172a;
|
||||
--text-sub: #334155;
|
||||
--text-muted: #64748b;
|
||||
--accent: #7e22ce;
|
||||
--metric-bg: #f1f5f9;
|
||||
--metric-border: #cbd5e1;
|
||||
}
|
||||
}
|
||||
.bg-panel { fill: var(--bg); stroke: var(--border); stroke-width: 1.2px; rx: 12px; }
|
||||
.title { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 13px; font-weight: 700; fill: var(--text-main); }
|
||||
.subtitle { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 11px; fill: var(--text-sub); }
|
||||
.lang-name { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 11px; font-weight: 600; fill: var(--text-main); }
|
||||
.lang-pct { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 11px; font-weight: 700; fill: var(--accent); }
|
||||
.lang-size { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; font-size: 10px; fill: var(--text-muted); }
|
||||
</style>
|
||||
""");
|
||||
|
||||
// Background
|
||||
sb.AppendLine($" <rect class=\"bg-panel\" x=\"1\" y=\"1\" width=\"{svgWidth - 2}\" height=\"{svgHeight - 2}\" />");
|
||||
|
||||
// Header
|
||||
sb.AppendLine(" <g transform=\"translate(24, 18)\">");
|
||||
sb.AppendLine(" <text class=\"title\" x=\"0\" y=\"14\">📈 Byte-genaue Sprachverteilung <tspan font-weight=\"400\" fill=\"var(--accent)\">•</tspan> <tspan font-weight=\"500\" font-size=\"11\" fill=\"var(--text-sub)\">Team-Codebase</tspan></text>");
|
||||
|
||||
string badgeText = $"Gesamt: {formattedTotal} analysierter Code";
|
||||
sb.AppendLine($" <rect x=\"445\" y=\"0\" width=\"255\" height=\"22\" rx=\"11\" fill=\"var(--metric-bg)\" stroke=\"var(--metric-border)\" stroke-width=\"1\" />");
|
||||
sb.AppendLine($" <circle cx=\"457\" cy=\"11\" r=\"4\" fill=\"var(--accent)\" />");
|
||||
sb.AppendLine($" <text class=\"subtitle\" x=\"467\" y=\"15\" font-weight=\"600\">{badgeText}</text>");
|
||||
sb.AppendLine(" </g>");
|
||||
|
||||
// Segmented Progress Bar
|
||||
sb.AppendLine(" <g clip-path=\"url(#mainLangClip)\">");
|
||||
double currentX = barStartX;
|
||||
foreach (var lang in languages)
|
||||
{
|
||||
double w = Math.Max(2.5, (lang.Percentage / 100.0) * barWidth);
|
||||
sb.AppendLine($" <rect x=\"{currentX.ToString("F1", CultureInfo.InvariantCulture)}\" y=\"50\" width=\"{w.ToString("F1", CultureInfo.InvariantCulture)}\" height=\"14\" fill=\"{lang.Color}\">");
|
||||
sb.AppendLine($" <title>{XmlEscape(lang.Name)}: {lang.Percentage:F1}% ({lang.FormattedSize})</title>");
|
||||
sb.AppendLine(" </rect>");
|
||||
currentX += w;
|
||||
}
|
||||
sb.AppendLine(" </g>");
|
||||
|
||||
// 3 Columns x 2 Rows Grid
|
||||
sb.AppendLine(" <!-- Language Legend Matrix -->");
|
||||
sb.AppendLine(" <g transform=\"translate(34, 84)\">");
|
||||
|
||||
int colWidth = 224;
|
||||
int rowHeight = 26;
|
||||
|
||||
for (int i = 0; i < languages.Count && i < 6; i++)
|
||||
{
|
||||
var l = languages[i];
|
||||
int col = i % 3;
|
||||
int row = i / 3;
|
||||
int x = col * colWidth;
|
||||
int y = row * rowHeight;
|
||||
|
||||
sb.AppendLine($" <g transform=\"translate({x}, {y})\">");
|
||||
sb.AppendLine($" <rect x=\"0\" y=\"0\" width=\"212\" height=\"22\" rx=\"6\" fill=\"var(--metric-bg)\" stroke=\"var(--metric-border)\" stroke-width=\"0.8\" />");
|
||||
sb.AppendLine($" <circle cx=\"12\" cy=\"11\" r=\"4.5\" fill=\"{l.Color}\" />");
|
||||
sb.AppendLine($" <text class=\"lang-name\" x=\"24\" y=\"15\">{XmlEscape(l.Name)}</text>");
|
||||
sb.AppendLine($" <text class=\"lang-pct\" x=\"110\" y=\"15\">{l.Percentage:F1}%</text>");
|
||||
sb.AppendLine($" <text class=\"lang-size\" x=\"155\" y=\"15\">({l.FormattedSize})</text>");
|
||||
sb.AppendLine(" </g>");
|
||||
}
|
||||
|
||||
sb.AppendLine(" </g>");
|
||||
|
||||
sb.AppendLine("</svg>");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string XmlEscape(string? text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return string.Empty;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user