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: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge)](LICENSE) -[![Python](https://img.shields.io/badge/Python-3.10+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/) -[![PySide6](https://img.shields.io/badge/Qt-6.9+-41C845?style=for-the-badge&logo=qt&logoColor=white)](https://qt.io/) -[![Platform](https://img.shields.io/badge/Platform-Win%20%7C%20macOS%20%7C%20Linux-silver?style=for-the-badge)](https://github.com/Agions/scene-fab/releases) -[![Version](https://img.shields.io/badge/v1.1.0-10B981?style=for-the-badge)](https://github.com/Agions/scene-fab/releases) +[![License: MIT](assets/badges/license-mit.svg)](LICENSE) +[![Python](assets/badges/python.svg)](https://www.python.org/) +[![PySide6](assets/badges/pyside6.svg)](https://qt.io/) +[![Platform](assets/badges/platform.svg)](https://github.com/Agions/scene-fab/releases) +[![Version](assets/badges/version.svg)](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) </div> + +<!-- +徽标说明:本仓库 README 中所有徽标均为自托管 SVG(位于 `assets/badges/`), +不依赖任何第三方徽标服务(如 shields.io)。优势: + - 100% 离线可用(无外网请求) + - 零隐私追踪 + - 风格与 SceneFab logo 系统统一(橙红渐变对应主题色) + - Version 徽标由 `scripts/generate_badges.py` 自动从 `pyproject.toml` 同步 +维护说明:bump 版本时运行 `python scripts/generate_badges.py` 即可。 +--> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" width="98" height="28" role="img" aria-label="License: MIT"> + <title>License: MIT + + + + + + + + + + + + + + + + + + License + MIT + + 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 @@ + + + Platform: Win | macOS | Linux + + + + + + + + + + + + + + + + + Platform + Win | macOS | Linux + + 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 @@ + + + PySide6 Qt 6.9+ + + + + + + + + + + + + + + + + + PySide6 + Qt 6.9 + + 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 @@ + + + Python 3.10+ + + + + + + + + + + + + + + + + + Python + 3.10+ + + 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 @@ + + + SceneFab v1.1.0 + + + + + + + + + + + + + + + + + version + v1.1.0 + + 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 @@ + + + License: MIT + + + + + + + + + + + + + + + + + + License + MIT + + 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 @@ + + + Platform: Win | macOS | Linux + + + + + + + + + + + + + + + + + Platform + Win | macOS | Linux + + 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 @@ + + + PySide6 Qt 6.9+ + + + + + + + + + + + + + + + + + PySide6 + Qt 6.9 + + 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 @@ + + + Python 3.10+ + + + + + + + + + + + + + + + + + Python + 3.10+ + + 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 @@ + + + SceneFab v1.1.0 + + + + + + + + + + + + + + + + + version + v1.1.0 + + 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''' + + SceneFab v{version} + + + + + + + + + + + + + + + + + version + v{version} + + +''' + 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())