feat: add official application icon across GUI, binary, and documentation
This commit is contained in:
parent
46dc6dc849
commit
96ad93b180
10 changed files with 86 additions and 5 deletions
|
|
@ -5,8 +5,8 @@ a = Analysis(
|
||||||
['patcher\\patcher_gui.py'],
|
['patcher\\patcher_gui.py'],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[('bettercf', 'bettercf'), ('patcher\\vendor', 'vendor'), ('patcher\\patch_core.py', '.')],
|
datas=[('bettercf', 'bettercf'), ('patcher\\vendor', 'vendor'), ('patcher\\patch_core.py', '.'), ('assets', 'assets')],
|
||||||
hiddenimports=[],
|
hiddenimports=['PIL', 'PIL.Image', 'PIL.ImageTk'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
hooksconfig={},
|
hooksconfig={},
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
|
|
@ -35,4 +35,5 @@ exe = EXE(
|
||||||
target_arch=None,
|
target_arch=None,
|
||||||
codesign_identity=None,
|
codesign_identity=None,
|
||||||
entitlements_file=None,
|
entitlements_file=None,
|
||||||
|
icon='assets\\icon.ico',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<div align="center">
|
<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.
|
### The modern, lightweight client modification for CurseForge Launcher.
|
||||||
|
|
||||||
|
|
|
||||||
BIN
assets/icon.ico
Normal file
BIN
assets/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/icon.png
Normal file
BIN
assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 667 KiB |
BIN
assets/icon_512.png
Normal file
BIN
assets/icon_512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
|
|
@ -9,7 +9,7 @@ export default defineConfig({
|
||||||
cleanUrls: true,
|
cleanUrls: true,
|
||||||
|
|
||||||
head: [
|
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: 'theme-color', content: '#9333ea' }],
|
||||||
['meta', { name: 'og:type', content: 'website' }],
|
['meta', { name: 'og:type', content: 'website' }],
|
||||||
['meta', { name: 'og:title', content: 'BetterCurseForge' }],
|
['meta', { name: 'og:title', content: 'BetterCurseForge' }],
|
||||||
|
|
@ -18,6 +18,7 @@ export default defineConfig({
|
||||||
|
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
siteTitle: 'BetterCurseForge',
|
siteTitle: 'BetterCurseForge',
|
||||||
|
logo: '/logo.png',
|
||||||
|
|
||||||
nav: [
|
nav: [
|
||||||
{ text: 'Guide', link: '/guide/installation' },
|
{ text: 'Guide', link: '/guide/installation' },
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ hero:
|
||||||
name: "BetterCurseForge"
|
name: "BetterCurseForge"
|
||||||
text: "Client mod for CurseForge Launcher"
|
text: "Client mod for CurseForge Launcher"
|
||||||
tagline: "Ad-free experience, custom themes, unlocked premium skins, and plugins."
|
tagline: "Ad-free experience, custom themes, unlocked premium skins, and plugins."
|
||||||
|
image:
|
||||||
|
src: /logo.png
|
||||||
|
alt: BetterCurseForge
|
||||||
actions:
|
actions:
|
||||||
- theme: brand
|
- theme: brand
|
||||||
text: Get Started
|
text: Get Started
|
||||||
|
|
|
||||||
BIN
docs/public/logo.png
Normal file
BIN
docs/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
|
|
@ -26,6 +26,7 @@ PERMANENT_DIR = Path(os.environ.get("LOCALAPPDATA", "")) / "BetterCurseForge"
|
||||||
PERMANENT_EXE = PERMANENT_DIR / "BetterCurseForge.exe"
|
PERMANENT_EXE = PERMANENT_DIR / "BetterCurseForge.exe"
|
||||||
PERMANENT_BCF = PERMANENT_DIR / "bettercf"
|
PERMANENT_BCF = PERMANENT_DIR / "bettercf"
|
||||||
PERMANENT_VENDOR = PERMANENT_DIR / "vendor"
|
PERMANENT_VENDOR = PERMANENT_DIR / "vendor"
|
||||||
|
PERMANENT_ASSETS = PERMANENT_DIR / "assets"
|
||||||
|
|
||||||
class GlobalNamedMutex:
|
class GlobalNamedMutex:
|
||||||
def __init__(self, name: str, timeout_ms: int = 5000):
|
def __init__(self, name: str, timeout_ms: int = 5000):
|
||||||
|
|
@ -247,6 +248,20 @@ def find_vendor_dir() -> Path | None:
|
||||||
return c
|
return c
|
||||||
return None
|
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]:
|
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
|
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.rmtree(PERMANENT_VENDOR, ignore_errors=True)
|
||||||
shutil.copytree(src_vendor, PERMANENT_VENDOR)
|
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):
|
if getattr(sys, "frozen", False):
|
||||||
curr_exe = Path(sys.executable).resolve()
|
curr_exe = Path(sys.executable).resolve()
|
||||||
if curr_exe != PERMANENT_EXE.resolve():
|
if curr_exe != PERMANENT_EXE.resolve():
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,12 @@ from tkinter import ttk, messagebox, scrolledtext
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import Image, ImageTk
|
||||||
|
HAS_PIL = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_PIL = False
|
||||||
|
|
||||||
# Import patch core logic
|
# Import patch core logic
|
||||||
try:
|
try:
|
||||||
from . import patch_core
|
from . import patch_core
|
||||||
|
|
@ -33,6 +39,20 @@ except ImportError:
|
||||||
|
|
||||||
VERSION = "1.0.0"
|
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)
|
# COLOR PALETTE (Modern Discord / Vencord Dark Theme)
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
@ -71,11 +91,34 @@ class BetterCFApp:
|
||||||
|
|
||||||
self.install_dir = None
|
self.install_dir = None
|
||||||
self.is_working = False
|
self.is_working = False
|
||||||
|
self._icon_photo = None
|
||||||
|
self._header_icon_photo = None
|
||||||
|
|
||||||
self._configure_styles()
|
self._configure_styles()
|
||||||
|
self._setup_window_icon()
|
||||||
self._build_ui()
|
self._build_ui()
|
||||||
self._refresh_status()
|
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):
|
def _configure_styles(self):
|
||||||
style = ttk.Style()
|
style = ttk.Style()
|
||||||
style.theme_use("clam")
|
style.theme_use("clam")
|
||||||
|
|
@ -97,8 +140,19 @@ class BetterCFApp:
|
||||||
title_frame = tk.Frame(header, bg=COLORS["bg_card"])
|
title_frame = tk.Frame(header, bg=COLORS["bg_card"])
|
||||||
title_frame.pack(side="left")
|
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_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"]
|
bg=COLORS["bg_card"], fg=COLORS["purple_glow"]
|
||||||
)
|
)
|
||||||
title_lbl.pack(side="left")
|
title_lbl.pack(side="left")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue