From 0f4e4b9c78badea46e3b611d39de340336aaa9c1 Mon Sep 17 00:00:00 2001
From: Agions <1051736049@qq.com>
Date: Tue, 2 Jun 2026 19:58:37 +0800
Subject: [PATCH] docs(badges): replace img.shields.io with native self-hosted
SVG badges
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Why
shields.io is great for prototypes, but for a shipped product it brings
real downsides:
- **Outage exposure** — if shields.io goes down, all 5 badges in our
README turn into red error icons
- **Network dependency** — offline / CI-behind-firewall users see broken
images
- **Privacy leak** — every README view sends a request with User-Agent
and Referer to a third-party CDN
- **Style drift** — third-party rendering can change without notice
- **Brand inconsistency** — the colors/typography don't match our
SceneFab 3-color logo system
## What changed
5 self-hosted SVG badges under `assets/badges/`, synced to
`docs/public/badges/` for the VitePress docs site:
| Badge | Path | Color story |
|-------|------|-------------|
| License: MIT | license-mit.svg | Indigo gradient (`#6366F1 → #4F46E5`) |
| Python 3.10+ | python.svg | Python blue (`#41A5DB → #2B7CB3`) |
| PySide6 Qt 6.9 | pyside6.svg | Qt green (`#52C752 → #2E8B2E`) |
| Platform | platform.svg | Neutral gray (`#9CA3AF → #6B7280`) |
| Version v1.1.0 | version.svg | **SceneFab brand orange-red** (`#ff8a5b → #e63946`) |
All 5 SVGs:
- 28px tall, for-the-badge style
- Verdana bold 11px text, accessible (role=img + aria-label +
)
- Validated as XML
## Automation
`scripts/generate_badges.py` reads the version from `pyproject.toml`
and regenerates `assets/badges/version.svg`:
```bash
python scripts/generate_badges.py # regenerate
python scripts/generate_badges.py --check # CI: exit 1 if drift
```
CI integration TODO: add a `make badges` or pre-commit hook so version
bumps always stay in sync.
## .gitignore fix
The previous `docs/` and `assets/` rules were too broad — they
prevented tracking any new resources in those directories. Replaced
with build-output-specific rules (`docs/build/`, `docs/.vitepress/dist/`,
`docs/.vitepress/cache/`, `assets/build/`, `assets/dist/`).
## Files
- 5 new badge SVGs in `assets/badges/`
- 5 sync copies in `docs/public/badges/` (VitePress)
- 1 new generator script: `scripts/generate_badges.py`
- README.md: 5 shields.io URLs → 5 local SVG paths + inline HTML
comment block documenting the decision
- .gitignore: build-artifact rules narrowed to actual build outputs
---
.gitignore | 7 ++-
README.md | 20 +++++--
assets/badges/license-mit.svg | 24 ++++++++
assets/badges/platform.svg | 23 +++++++
assets/badges/pyside6.svg | 23 +++++++
assets/badges/python.svg | 23 +++++++
assets/badges/version.svg | 23 +++++++
docs/public/badges/license-mit.svg | 24 ++++++++
docs/public/badges/platform.svg | 23 +++++++
docs/public/badges/pyside6.svg | 23 +++++++
docs/public/badges/python.svg | 23 +++++++
docs/public/badges/version.svg | 23 +++++++
scripts/generate_badges.py | 96 ++++++++++++++++++++++++++++++
13 files changed, 348 insertions(+), 7 deletions(-)
create mode 100644 assets/badges/license-mit.svg
create mode 100644 assets/badges/platform.svg
create mode 100644 assets/badges/pyside6.svg
create mode 100644 assets/badges/python.svg
create mode 100644 assets/badges/version.svg
create mode 100644 docs/public/badges/license-mit.svg
create mode 100644 docs/public/badges/platform.svg
create mode 100644 docs/public/badges/pyside6.svg
create mode 100644 docs/public/badges/python.svg
create mode 100644 docs/public/badges/version.svg
create mode 100644 scripts/generate_badges.py
diff --git a/.gitignore b/.gitignore
index d1ce3dc8..a20704be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -384,5 +384,8 @@ ARCHITECTURE.md
_DEAD/
# Build artifacts
-docs/
-assets/
+docs/build/
+docs/.vitepress/dist/
+docs/.vitepress/cache/
+assets/build/
+assets/dist/
diff --git a/README.md b/README.md
index 6111df8d..d9bdaae0 100644
--- a/README.md
+++ b/README.md
@@ -7,11 +7,11 @@
> **上传一部电影 → AI 自动完成语义拆条、解说稿、配音、字幕、合成导出**
> 从「几天一条」变成「一天十条」。
-[](LICENSE)
-[](https://www.python.org/)
-[](https://qt.io/)
-[](https://github.com/Agions/scene-fab/releases)
-[](https://github.com/Agions/scene-fab/releases)
+[](LICENSE)
+[](https://www.python.org/)
+[](https://qt.io/)
+[](https://github.com/Agions/scene-fab/releases)
+[](https://github.com/Agions/scene-fab/releases)
[**在线文档**](https://agions.github.io/scene-fab/) · [**下载安装**](https://github.com/Agions/scene-fab/releases) · [**报告问题**](https://github.com/Agions/scene-fab/issues/new) · [**功能建议**](https://github.com/Agions/scene-fab/discussions)
@@ -239,3 +239,13 @@ SceneFab 的诞生离不开以下开源项目:
⭐ 如果 SceneFab 对你有帮助,请给一个 Star · 🐛 遇到问题请提交 [Issue](https://github.com/Agions/scene-fab/issues)
+
+
diff --git a/assets/badges/license-mit.svg b/assets/badges/license-mit.svg
new file mode 100644
index 00000000..9266130a
--- /dev/null
+++ b/assets/badges/license-mit.svg
@@ -0,0 +1,24 @@
+
+
diff --git a/assets/badges/platform.svg b/assets/badges/platform.svg
new file mode 100644
index 00000000..1e62f0e4
--- /dev/null
+++ b/assets/badges/platform.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/assets/badges/pyside6.svg b/assets/badges/pyside6.svg
new file mode 100644
index 00000000..a3dc340f
--- /dev/null
+++ b/assets/badges/pyside6.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/assets/badges/python.svg b/assets/badges/python.svg
new file mode 100644
index 00000000..33b7b438
--- /dev/null
+++ b/assets/badges/python.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/assets/badges/version.svg b/assets/badges/version.svg
new file mode 100644
index 00000000..fd606dc1
--- /dev/null
+++ b/assets/badges/version.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/docs/public/badges/license-mit.svg b/docs/public/badges/license-mit.svg
new file mode 100644
index 00000000..9266130a
--- /dev/null
+++ b/docs/public/badges/license-mit.svg
@@ -0,0 +1,24 @@
+
+
diff --git a/docs/public/badges/platform.svg b/docs/public/badges/platform.svg
new file mode 100644
index 00000000..1e62f0e4
--- /dev/null
+++ b/docs/public/badges/platform.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/docs/public/badges/pyside6.svg b/docs/public/badges/pyside6.svg
new file mode 100644
index 00000000..a3dc340f
--- /dev/null
+++ b/docs/public/badges/pyside6.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/docs/public/badges/python.svg b/docs/public/badges/python.svg
new file mode 100644
index 00000000..33b7b438
--- /dev/null
+++ b/docs/public/badges/python.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/docs/public/badges/version.svg b/docs/public/badges/version.svg
new file mode 100644
index 00000000..fd606dc1
--- /dev/null
+++ b/docs/public/badges/version.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/scripts/generate_badges.py b/scripts/generate_badges.py
new file mode 100644
index 00000000..1003e66f
--- /dev/null
+++ b/scripts/generate_badges.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+"""Generate SceneFab version badge SVG from pyproject.toml.
+
+Usage:
+ python scripts/generate_badges.py
+ python scripts/generate_badges.py --version 1.2.0
+ python scripts/generate_badges.py --check # CI check (fail if drift)
+"""
+
+from __future__ import annotations
+
+import argparse
+import re
+import sys
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parent.parent
+PYPROJECT = ROOT / "pyproject.toml"
+BADGE_PATH = ROOT / "assets" / "badges" / "version.svg"
+
+
+def read_pyproject_version() -> str:
+ """Extract `version = "X.Y.Z"` from pyproject.toml."""
+ if not PYPROJECT.exists():
+ sys.exit(f"❌ pyproject.toml not found at {PYPROJECT}")
+ content = PYPROJECT.read_text(encoding="utf-8")
+ match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
+ if not match:
+ sys.exit(f"❌ No version field found in {PYPROJECT}")
+ return match.group(1)
+
+
+def render_version_badge(version: str) -> str:
+ """Render the version badge SVG with the given version string."""
+ svg = f'''
+
+'''
+ return svg
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description="Generate SceneFab version badge.")
+ parser.add_argument(
+ "--version", help="Override version (default: read from pyproject.toml)"
+ )
+ parser.add_argument(
+ "--check",
+ action="store_true",
+ help="CI check: exit 1 if badge is out of sync with pyproject.toml",
+ )
+ args = parser.parse_args()
+
+ version = args.version or read_pyproject_version()
+
+ if args.check:
+ if not BADGE_PATH.exists():
+ print(f"❌ Badge not found at {BADGE_PATH}")
+ return 1
+ existing = BADGE_PATH.read_text(encoding="utf-8")
+ expected = render_version_badge(version)
+ if existing == expected:
+ print(f"✅ Badge in sync (v{version})")
+ return 0
+ print(f"❌ Badge drift: expected v{version}, file is out of sync")
+ print(f" Run: python scripts/generate_badges.py")
+ return 1
+
+ BADGE_PATH.parent.mkdir(parents=True, exist_ok=True)
+ BADGE_PATH.write_text(render_version_badge(version), encoding="utf-8")
+ print(f"✅ Wrote {BADGE_PATH} (v{version})")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())