feat: add official application icon across GUI, binary, and documentation

This commit is contained in:
tomasalias 2026-10-08 21:18:55 +02:00
parent 46dc6dc849
commit 96ad93b180
10 changed files with 86 additions and 5 deletions

View file

@ -5,8 +5,8 @@ a = Analysis(
['patcher\\patcher_gui.py'],
pathex=[],
binaries=[],
datas=[('bettercf', 'bettercf'), ('patcher\\vendor', 'vendor'), ('patcher\\patch_core.py', '.')],
hiddenimports=[],
datas=[('bettercf', 'bettercf'), ('patcher\\vendor', 'vendor'), ('patcher\\patch_core.py', '.'), ('assets', 'assets')],
hiddenimports=['PIL', 'PIL.Image', 'PIL.ImageTk'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
@ -35,4 +35,5 @@ exe = EXE(
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='assets\\icon.ico',
)

View file

@ -2,7 +2,7 @@
<div align="center">
<img src="docs/public/logo.svg" alt="BetterCurseForge Logo" width="128" height="128" />
<img src="assets/icon_512.png" alt="BetterCurseForge Logo" width="128" height="128" />
### The modern, lightweight client modification for CurseForge Launcher.

BIN
assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 KiB

BIN
assets/icon_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View file

@ -9,7 +9,7 @@ export default defineConfig({
cleanUrls: true,
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }],
['link', { rel: 'icon', type: 'image/png', href: (process.env.BASE_URL || '/BetterCurseForge/') + 'logo.png' }],
['meta', { name: 'theme-color', content: '#9333ea' }],
['meta', { name: 'og:type', content: 'website' }],
['meta', { name: 'og:title', content: 'BetterCurseForge' }],
@ -18,6 +18,7 @@ export default defineConfig({
themeConfig: {
siteTitle: 'BetterCurseForge',
logo: '/logo.png',
nav: [
{ text: 'Guide', link: '/guide/installation' },

View file

@ -5,6 +5,9 @@ hero:
name: "BetterCurseForge"
text: "Client mod for CurseForge Launcher"
tagline: "Ad-free experience, custom themes, unlocked premium skins, and plugins."
image:
src: /logo.png
alt: BetterCurseForge
actions:
- theme: brand
text: Get Started

BIN
docs/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View file

@ -26,6 +26,7 @@ PERMANENT_DIR = Path(os.environ.get("LOCALAPPDATA", "")) / "BetterCurseForge"
PERMANENT_EXE = PERMANENT_DIR / "BetterCurseForge.exe"
PERMANENT_BCF = PERMANENT_DIR / "bettercf"
PERMANENT_VENDOR = PERMANENT_DIR / "vendor"
PERMANENT_ASSETS = PERMANENT_DIR / "assets"
class GlobalNamedMutex:
def __init__(self, name: str, timeout_ms: int = 5000):
@ -247,6 +248,20 @@ def find_vendor_dir() -> Path | None:
return c
return None
def find_assets_dir() -> Path | None:
candidates = [
Path(getattr(sys, "_MEIPASS", "")) / "assets" if hasattr(sys, "_MEIPASS") else None,
Path(sys.executable).parent / "assets" if getattr(sys, "frozen", False) else None,
PERMANENT_ASSETS,
Path(__file__).parent / "assets",
Path(__file__).parent.parent / "assets",
Path.cwd() / "assets",
]
for c in candidates:
if c and c.exists() and c.is_dir():
return c
return None
def run_asar_tool(args: list[str], cf_dir: Path | None = None) -> tuple[bool, str]:
"""
Executes asar pack/extract offline using bundled @electron/asar with either
@ -775,6 +790,13 @@ def ensure_permanent_installation() -> Path:
shutil.rmtree(PERMANENT_VENDOR, ignore_errors=True)
shutil.copytree(src_vendor, PERMANENT_VENDOR)
src_assets = find_assets_dir()
if src_assets and src_assets.exists():
if PERMANENT_ASSETS.resolve() != src_assets.resolve():
if PERMANENT_ASSETS.exists():
shutil.rmtree(PERMANENT_ASSETS, ignore_errors=True)
shutil.copytree(src_assets, PERMANENT_ASSETS)
if getattr(sys, "frozen", False):
curr_exe = Path(sys.executable).resolve()
if curr_exe != PERMANENT_EXE.resolve():

View file

@ -25,6 +25,12 @@ from tkinter import ttk, messagebox, scrolledtext
from pathlib import Path
from datetime import datetime
try:
from PIL import Image, ImageTk
HAS_PIL = True
except ImportError:
HAS_PIL = False
# Import patch core logic
try:
from . import patch_core
@ -33,6 +39,20 @@ except ImportError:
VERSION = "1.0.0"
def find_asset(filename: str) -> Path | None:
candidates = [
Path(getattr(sys, "_MEIPASS", "")) / "assets" / filename if hasattr(sys, "_MEIPASS") else None,
Path(getattr(sys, "_MEIPASS", "")) / filename if hasattr(sys, "_MEIPASS") else None,
Path(sys.executable).parent / "assets" / filename if getattr(sys, "frozen", False) else None,
getattr(patch_core, "PERMANENT_ASSETS", Path()) / filename,
Path(__file__).parent.parent / "assets" / filename,
Path.cwd() / "assets" / filename,
]
for c in candidates:
if c and c.exists():
return c
return None
# ═══════════════════════════════════════════════════════════════════════════════
# COLOR PALETTE (Modern Discord / Vencord Dark Theme)
# ═══════════════════════════════════════════════════════════════════════════════
@ -71,11 +91,34 @@ class BetterCFApp:
self.install_dir = None
self.is_working = False
self._icon_photo = None
self._header_icon_photo = None
self._configure_styles()
self._setup_window_icon()
self._build_ui()
self._refresh_status()
def _setup_window_icon(self):
ico_path = find_asset("icon.ico")
if ico_path and os.name == "nt":
try:
self.root.iconbitmap(default=str(ico_path.resolve()))
except Exception:
pass
png_path = find_asset("icon_512.png") or find_asset("icon.png")
if png_path:
try:
if HAS_PIL:
im = Image.open(png_path)
self._icon_photo = ImageTk.PhotoImage(im.resize((64, 64), Image.Resampling.LANCZOS))
self.root.iconphoto(True, self._icon_photo)
else:
self._icon_photo = tk.PhotoImage(file=str(png_path))
self.root.iconphoto(True, self._icon_photo)
except Exception:
pass
def _configure_styles(self):
style = ttk.Style()
style.theme_use("clam")
@ -97,8 +140,19 @@ class BetterCFApp:
title_frame = tk.Frame(header, bg=COLORS["bg_card"])
title_frame.pack(side="left")
# Header icon thumbnail
png_path = find_asset("icon_512.png") or find_asset("icon.png")
if png_path and HAS_PIL:
try:
header_img = Image.open(png_path).resize((32, 32), Image.Resampling.LANCZOS)
self._header_icon_photo = ImageTk.PhotoImage(header_img)
icon_lbl = tk.Label(title_frame, image=self._header_icon_photo, bg=COLORS["bg_card"])
icon_lbl.pack(side="left", padx=(0, 10))
except Exception:
pass
title_lbl = tk.Label(
title_frame, text="⬡ BetterCurseForge", font=("Segoe UI", 16, "bold"),
title_frame, text="BetterCurseForge", font=("Segoe UI", 16, "bold"),
bg=COLORS["bg_card"], fg=COLORS["purple_glow"]
)
title_lbl.pack(side="left")