Compare commits

..

No commits in common. "fed62b18bcecc57aac987b78cf38352595925c82" and "c66cd9484a0683e4b0ee2adb32375040a2296fca" have entirely different histories.

8 changed files with 201 additions and 205 deletions

2
.gitignore vendored
View file

@ -41,6 +41,4 @@ desktop.ini
tools/
update_icon.bat
update_icon.py
docs_for_claude.json

View file

@ -90,7 +90,7 @@ and `themes/my-theme.css`:
### Writing a plugin
A plugin lives in its own subfolder, `plugins/<plugin-name>/`, with a `manifest.json`, an `index.js`, and optionally a `styles.css`.
A plugin lives in its own subfolder in `plugins/<plugin-name>/` with a `manifest.json`, an `index.js`, and an optional companion `styles.css`.
`plugins/my-plugin/manifest.json`:
@ -121,10 +121,10 @@ plugin.onDisable = () => {
#### What plugins can do
- **UI and DOM tweaks.** Add buttons, badges, links or panels with the normal DOM APIs. If the plugin folder has a `styles.css`, it's injected when the plugin is enabled and removed when it's disabled.
- **Method patching.** Hook into or replace internal JavaScript functions with `BetterCurseForge.patcher.before()`, `after()` or `instead()`.
- **Persistent settings.** Save and read preferences across restarts with `BetterCurseForge.settings.get(key)` and `set(key, value)`.
- **Events.** Listen for app lifecycle events with `BetterCurseForge.on(event, callback)`.
- **UI & DOM tweaks.** Inject custom buttons, badges, links, or panels into CurseForge using standard DOM APIs. If a `styles.css` is present in the plugin folder, it is automatically injected when enabled and removed when disabled.
- **Method patching.** Intercept, modify, or override internal JavaScript functions with `BetterCurseForge.patcher.before()`, `after()`, or `instead()`.
- **Persistent settings.** Store and read user preferences across restarts using `BetterCurseForge.settings.get(key)` and `set(key, value)`.
- **Event listening.** Hook into application lifecycle events using `BetterCurseForge.on(event, callback)`.
## Building from source
@ -142,7 +142,7 @@ The executable ends up in `dist/BetterCurseForge.exe`.
### Syncing changes from GitHub
To pull changes from GitHub:
To pull down updates made on GitHub:
```powershell
git pull origin master

View file

@ -3,13 +3,13 @@ import { defineConfig } from 'vitepress';
export default defineConfig({
title: 'BetterCurseForge',
description: 'Client mod for CurseForge Launcher',
base: process.env.BASE_URL || '/',
base: process.env.BASE_URL || '/BetterCurseForge/',
appearance: 'dark',
lastUpdated: true,
cleanUrls: true,
head: [
['link', { rel: 'icon', type: 'image/png', href: (process.env.BASE_URL || '/') + 'logo.png' }],
['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' }],
@ -23,7 +23,6 @@ export default defineConfig({
nav: [
{ text: 'Guide', link: '/guide/installation' },
{ text: 'API', link: '/api/' },
{ text: 'Forgejo', link: 'http://92.5.113.44:3000/tomasalias/BetterCurseForge' },
{ text: 'GitHub', link: 'https://github.com/tomasalias/BetterCurseForge' },
],

View file

@ -1,12 +1,12 @@
# API Reference
BetterCurseForge adds a global object, `window.BetterCurseForge`, to CurseForge's Electron renderer process. Themes, plugins and your own scripts can use it to inject styles, patch methods, read and write settings, and listen for events.
BetterCurseForge exposes a global runtime object on `window.BetterCurseForge` within the CurseForge Electron renderer process. This API allows themes, plugins, and developer scripts to inject styles, monkey-patch methods, manage settings, and listen to application events.
---
## TypeScript definitions
## TypeScript Definition
If you're using TypeScript, this gives you types and autocompletion:
For TypeScript development and autocompletion:
```typescript
declare global {
@ -54,7 +54,7 @@ export interface BetterCurseForgeAPI {
### `BetterCurseForge.version`
- **Type**: `string`
- **Description**: Version of the BetterCurseForge runtime that's currently running.
- **Description**: The currently running BetterCurseForge runtime version.
```javascript
console.log(BetterCurseForge.version); // "1.0.0"
@ -64,11 +64,11 @@ console.log(BetterCurseForge.version); // "1.0.0"
### `BetterCurseForge.injectCSS(css)`
Adds CSS rules to the document `<head>`.
Dynamically injects CSS rules into the document `<head>`.
- **Parameters**:
- `css` *(string)*: The CSS to inject.
- **Returns**: `() => void`. Call it to remove the injected `<style>` tag.
- `css` *(string)*: Raw CSS string to inject.
- **Returns**: `() => void` — Function that removes the injected `<style>` tag when called.
```javascript
// Inject styles
@ -86,15 +86,15 @@ uninject();
## Settings API (`BetterCurseForge.settings`)
Persistent configuration, stored in `%APPDATA%\CurseForge\BetterCurseForge\settings.json`.
Manages persistent configuration stored in `%APPDATA%\CurseForge\BetterCurseForge\settings.json`.
### `settings.get(key?)`
Returns the setting for `key`, or the whole settings object if you don't pass a key.
Retrieves a setting by key, or returns the entire settings object if no key is specified.
- **Parameters**:
- `key` *(string, optional)*: Name of the setting.
- **Returns**: `Promise<any>`. The value, or `null` if it isn't set.
- `key` *(string, optional)*: Setting key name.
- **Returns**: `Promise<any>` — Value of the setting, or `null`.
```javascript
// Retrieve a specific value
@ -106,12 +106,12 @@ const allSettings = await BetterCurseForge.settings.get();
### `settings.set(key, value)`
Saves a value and writes it to disk asynchronously.
Stores a setting value and writes it asynchronously to disk.
- **Parameters**:
- `key` *(string)*: Name of the setting.
- `value` *(any)*: Any JSON-serializable value.
- **Returns**: `Promise<boolean>`. `true` if the save worked.
- `key` *(string)*: Setting key name.
- `value` *(any)*: JSON-serializable value.
- **Returns**: `Promise<boolean>` — `true` if saved successfully.
```javascript
await BetterCurseForge.settings.set('myPlugin.fontSize', 14);
@ -121,17 +121,17 @@ await BetterCurseForge.settings.set('myPlugin.fontSize', 14);
## Patcher API (`BetterCurseForge.patcher`)
Utilities for inspecting, modifying or replacing methods on JavaScript objects and modules. Every function returns an unpatch function.
Provides monkey-patching utilities to inspect, modify, or replace methods on JavaScript objects and modules safely.
### `patcher.before(obj, method, callback)`
Runs `callback(args)` **before** the target method is called.
Executes `callback(args)` **before** the target method is invoked.
- **Parameters**:
- `obj` *(object)*: Target object.
- `method` *(string)*: Method name on `obj`.
- `callback` *(Function)*: `(args) => void`.
- **Returns**: `() => void`. Call it to restore the original method.
- `callback` *(Function)*: Function receiving arguments: `(args) => void`.
- **Returns**: `() => void` — Unpatch function that restores the original method.
```javascript
const unpatch = BetterCurseForge.patcher.before(console, 'warn', (args) => {
@ -146,13 +146,13 @@ unpatch();
### `patcher.after(obj, method, callback)`
Runs `callback(args, result)` **after** the target method. Return a value to replace the method's return value.
Executes `callback(args, result)` **after** the target method runs. You can return a new value to replace the method's return value.
- **Parameters**:
- `obj` *(object)*: Target object.
- `method` *(string)*: Method name on `obj`.
- `callback` *(Function)*: `(args, result) => any`.
- **Returns**: `() => void`. Unpatch function.
- **Returns**: `() => void` — Unpatch function.
```javascript
const unpatch = BetterCurseForge.patcher.after(service, 'getTitle', (args, result) => {
@ -166,13 +166,13 @@ unpatch();
### `patcher.instead(obj, method, callback)`
Replaces the target method. The callback gets the arguments and a reference to the original method.
Completely overrides the target method. Receives both the incoming arguments and a reference to the original method.
- **Parameters**:
- `obj` *(object)*: Target object.
- `method` *(string)*: Method name on `obj`.
- `callback` *(Function)*: `(args, originalFn) => any`.
- **Returns**: `() => void`. Unpatch function.
- **Returns**: `() => void` — Unpatch function.
```javascript
const unpatch = BetterCurseForge.patcher.instead(target, 'openExternalUrl', (args, originalFn) => {
@ -188,12 +188,12 @@ unpatch();
---
## Event system (`BetterCurseForge.on / off / emit`)
## Event System (`BetterCurseForge.on / off / emit`)
BetterCurseForge has an event bus that plugins can use to talk to each other and to react to lifecycle events.
BetterCurseForge includes an event bus for inter-plugin and lifecycle communication.
### `BetterCurseForge.on(event, callback)`
Adds a listener for an event.
Subscribes a listener to an event.
```javascript
const onReady = () => console.log('BetterCurseForge is ready!');
@ -201,14 +201,14 @@ BetterCurseForge.on('ready', onReady);
```
### `BetterCurseForge.off(event, callback)`
Removes a listener.
Unsubscribes a listener.
```javascript
BetterCurseForge.off('ready', onReady);
```
### `BetterCurseForge.emit(event, ...args)`
Calls every listener registered for the event.
Emits an event to all registered listeners.
```javascript
BetterCurseForge.emit('custom-event', { detail: 123 });
@ -216,10 +216,10 @@ BetterCurseForge.emit('custom-event', { detail: 123 });
---
## Subsystem managers
## Subsystem Managers
### `BetterCurseForge.themes` (`ThemeManager`)
Control custom themes from code:
Provides programmatic control over custom themes:
```javascript
// Get all discovered themes
@ -242,7 +242,7 @@ BetterCurseForge.themes.removeTheme(true);
```
### `BetterCurseForge.plugins` (`PluginManager`)
Control plugins from code:
Provides programmatic control over plugins:
```javascript
// Get all installed plugins
@ -253,7 +253,7 @@ await BetterCurseForge.plugins.togglePlugin('compact-mode');
```
### `BetterCurseForge.ads` (`AdBlocker`)
The internal ad blocker runtime:
Internal ad blocker runtime:
```javascript
// Contains zero-ad CSS rules and ad container cleanup handlers

View file

@ -1,51 +1,51 @@
# Installation & Usage
How to install, check, and remove **BetterCurseForge**, using either the desktop app or the command line.
This guide covers installing, managing, and uninstalling **BetterCurseForge** using either the desktop GUI application or the command-line interface.
---
## Prerequisites
You need:
Before using BetterCurseForge, make sure you have:
- **Windows 10 or Windows 11** (64-bit)
- **CurseForge Launcher** installed (standalone or Overwolf edition)
- CurseForge closed when you run the patcher
- CurseForge closed before running the patcher
::: tip BACKUPS
Before it changes anything, BetterCurseForge saves untouched copies of the original files at `resources/app.asar.vanilla` and `CurseForge.exe.vanilla`. You can go back to stock CurseForge whenever you want, with one click.
::: tip SAFE & 100% REVERSIBLE
Before modifying any files, BetterCurseForge creates untouched vanilla backups at `resources/app.asar.vanilla` and `CurseForge.exe.vanilla`. You can cleanly revert to stock CurseForge at any time with a single click.
:::
---
## Option 1: Desktop GUI (recommended)
## Method 1: Using the Desktop GUI (Recommended)
BetterCurseForge is a single executable with a dark-themed GUI.
BetterCurseForge comes as a standalone desktop executable with a sleek, dark-themed graphical interface.
### Step 1: Download `BetterCurseForge.exe`
Grab the latest build from the releases page:
Download the latest executable from the official releases:
- [Download `BetterCurseForge.exe`](https://github.com/tomasalias/BetterCurseForge/releases/latest)
Put it anywhere you like (Downloads, a tools folder, the desktop).
Place it in any convenient folder (e.g., Downloads, Tools, or desktop).
### Step 2: Open BetterCurseForge
Double-click `BetterCurseForge.exe` to open it.
Double-click `BetterCurseForge.exe` to launch the application.
![BetterCurseForge GUI](/logo.png){width=64 style="display:block;margin:1rem auto;"}
On startup it:
- Finds your CurseForge install folder through the Windows Registry.
- Checks whether CurseForge is vanilla or patched.
- Shows the installed CurseForge version and install directory.
The application automatically:
- Detects your CurseForge installation folder from the Windows Registry.
- Checks whether your client is currently vanilla or patched.
- Displays the installed CurseForge version and installation directory.
### Step 3: Click "Patch CurseForge"
1. Click the **Patch CurseForge** button.
2. The patcher closes any running CurseForge processes, backs up the vanilla files, applies the patch (ad blocking, theme engine, skin unlocks, plugin loader), updates the integrity signature in the executable, and repacks the app.
3. The log panel shows progress as it goes:
2. The patcher will close any running CurseForge processes, create a vanilla backup, apply all enhancements (ad removal, theme engine, premium unlocks, and plugin loader), update the executable integrity signature, and repack the application.
3. You will see live output in the log panel confirming:
```text
[+] Found CurseForge at: C:\Users\Username\AppData\Local\Programs\CurseForge Windows
[+] Unlocked native premium themes (2 route checks)
@ -60,17 +60,17 @@ On startup it:
[+] Auto-repatch watcher active and persistent across updates.
[*] CurseForge successfully patched! Ads removed, themes & plugins enabled.
```
4. Click **Launch CurseForge** or start it from your usual shortcut. That's it.
4. Click **Launch CurseForge** or open your usual desktop shortcut—you're done!
---
## Option 2: Command line
## Method 2: Command-Line Interface (CLI)
If you want to script it or run it without the GUI, `BetterCurseForge.exe` has a CLI.
For automated workflows, scripting, or headless environments, `BetterCurseForge.exe` provides a comprehensive CLI:
### Patching
### Patching via Terminal
Open PowerShell or Command Prompt in the folder with `BetterCurseForge.exe`:
Open PowerShell or Command Prompt in the folder containing `BetterCurseForge.exe`:
::: code-group
@ -84,9 +84,9 @@ BetterCurseForge.exe patch
:::
### Checking status
### Checking Status
See whether CurseForge is patched:
Verify whether your client is patched:
```powershell
.\BetterCurseForge.exe status
@ -101,26 +101,26 @@ ASAR Hash: Verified
### Unpatching
Go back to stock CurseForge:
Restore your vanilla CurseForge client:
```powershell
.\BetterCurseForge.exe unpatch
```
This restores `resources/app.asar` from the vanilla backup and re-verifies the original executable signatures.
This restores `resources/app.asar` from the vanilla backup and re-verifies original executable signatures.
---
## Auto-repatch after CurseForge updates
## Auto-Repatch: Surviving CurseForge Updates
When CurseForge updates itself, it overwrites `app.asar` with a stock copy and restarts.
When the official CurseForge Launcher downloads an automatic update, it overwrites `app.asar` with an unpatched version and attempts to restart.
BetterCurseForge handles this without the unpatched launcher ever showing up on screen.
BetterCurseForge provides an autonomous, smooth transition with **zero screen flashing**:
### Managing it in the GUI
- Toggle the **Auto-Repatch on Update** button in the app. It turns green when it's on.
### Managing Auto-Repatch in the GUI
- In the BetterCurseForge desktop application, simply toggle the **Auto-Repatch on Update** button (turns green when active).
### Managing it from the CLI
### Managing Auto-Repatch via CLI
```powershell
# Run the background watcher daemon directly:
.\BetterCurseForge.exe watch
@ -133,18 +133,18 @@ BetterCurseForge handles this without the unpatched launcher ever showing up on
.\BetterCurseForge.exe unpatch # Restores vanilla client and stops watcher
```
### How it works
1. **Background watcher**: Runs in the background on Windows directory change notifications and periodic heartbeat checks, so CPU use is negligible.
2. **Killing the stock launcher**: Once it sees an update or an unpatched install, the watcher polls every 150 ms with Win32 process enumeration. If the installer starts `CurseForge.exe`, the watcher kills it within 50 ms, before a window can appear.
3. **Splash window**: A dark `#121214` card shows the app icon, a purple `AUTO-UPDATE` badge, a progress bar, and status messages while it extracts, unlocks themes, blocks ads and repacks.
4. **Offline repatch**: Extracting and repacking the ASAR takes 2–3 seconds and needs no internet. It uses the Node v24 runtime bundled with CurseForge (`ELECTRON_RUN_AS_NODE=1`) and writes the new `OWEASARSIG` hash into `CurseForge.exe`.
5. **Relaunch**: The patched CurseForge opens and the splash window closes.
### How Auto-Repatch Works
1. **Background Watcher**: Runs silently with 0% CPU consumption via Windows directory event notifications and heartbeat checks.
2. **Active Vanilla Interceptor**: When an update or unpatched state is detected, the patcher polls every 150ms using Win32 kernel process enumeration. If the installer launches `CurseForge.exe`, it is killed within 50ms before any window or frame can appear.
3. **Dedicated Splash Updater GUI**: A modern, dark-themed `#121214` card appears on screen displaying the application icon, a purple `AUTO-UPDATE` badge, an animated progress bar, and real-time status messages while extracting, unlocking themes, removing ads, and repacking.
4. **Offline Zero-Delay Engine**: Offline ASAR extraction and repacking complete in 2–3 seconds using CurseForge's built-in Node v24 runtime (`ELECTRON_RUN_AS_NODE=1`), synchronizing the `OWEASARSIG` hash directly inside `CurseForge.exe`.
5. **Seamless Relaunch**: The patched BetterCurseForge client opens automatically, and the splash window gracefully closes.
---
## Under the hood
## How It Works Under the Hood
Recent CurseForge versions verify the integrity of `app.asar` from inside `CurseForge.exe`. This is the patch flow:
Modern versions of CurseForge include **ASAR Code Integrity Verification** enforced directly inside `CurseForge.exe`:
```mermaid
flowchart TD
@ -158,19 +158,19 @@ flowchart TD
H --> I["Launch Modded CurseForge with Full Permissions"]
```
1. **Ad blocking**: Ad requests are blocked at the Electron session level (`session.webRequest`), and CSS collapses the ad frames to 0px. The elements stay in the DOM, so React Fiber's reconciliation isn't affected.
2. **Skin unlock**: AST and regex matching, written to tolerate version changes, unlocks the official CurseForge skins (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Halloween*, etc.) in **Settings -> Themes**.
3. **ASAR signature update**: It computes the SHA256 hash of the modified ASAR header and writes it into `CurseForge.exe`, so Electron starts without integrity errors.
1. **Multi-Layer Ad Elimination**: Blocks ad network requests at the Electron session level (`session.webRequest`) and collapses ad frames to 0px using CSS rules, completely preserving React Fiber virtual DOM reconciliation.
2. **Premium Skin Unlock**: Uses version-resilient AST/regex matching to unlock all official CurseForge skins (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Halloween*, etc.) in **Settings -> Themes**.
3. **ASAR Signature Synchronization**: Calculates the cryptographic SHA256 hash of the modified ASAR archive header and writes the new signature directly into `CurseForge.exe`, ensuring Electron boots without security errors or corruption alerts.
---
## Troubleshooting
## Troubleshooting & FAQ
::: details How do I tell it's working?
::: details How do I verify BetterCurseForge is running?
1. Open CurseForge.
2. There should be no banner ads or video sponsor frames.
3. Go to **Settings -> Themes**. You should see a **Client Themes** section with *Purple Night* in it, and the *Premium* skins (*Pixelforge*, *Lifeforge*, etc.) unlocked.
4. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd> to open DevTools. The console should print:
2. Notice the total absence of banner ads and video sponsor frames.
3. Navigate to **Settings -> Themes**: you will see the **Client Themes** section with *Purple Night* available, as well as unlocked *Premium* skins (*Pixelforge*, *Lifeforge*, etc.).
4. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd> to open DevTools. The console will display:
```text
[BetterCF] Initializing renderer runtime...
[BetterCF] Theme manager ready (1 theme(s) found). Active: purple-night.css
@ -178,10 +178,10 @@ flowchart TD
```
:::
::: details Antivirus flags the exe
Some antivirus tools flag `BetterCurseForge.exe` as a false positive. It looks at running processes (`CurseForge.exe`), reads the Windows Registry to find the install path, and rewrites hashes in an executable, which is the kind of thing heuristic scanners react to. The project is open source: the Python code is in `patcher/`, and you can read it and build the exe yourself.
::: details Antivirus false-positive warning
Because `BetterCurseForge.exe` inspects running processes (`CurseForge.exe`), accesses the Windows Registry to locate install paths, and updates executable hashes, some heuristic scanners may flag it. BetterCurseForge is completely open source and safe. You can inspect the entire Python codebase in `patcher/` and build it yourself.
:::
::: details Permission denied / EPERM error
If CurseForge is installed under `C:\Program Files`, the patcher needs Administrator permissions to change files. Right-click `BetterCurseForge.exe` and choose **Run as Administrator**.
If your CurseForge launcher is installed under `C:\Program Files`, modifying files requires Administrator permissions. Right-click `BetterCurseForge.exe` and select **Run as Administrator**.
:::

View file

@ -1,18 +1,18 @@
# Writing Plugins
Plugins are small JavaScript modules that run inside CurseForge. They can change the DOM, patch methods at runtime, and save settings between sessions.
BetterCurseForge features a lightweight plugin system that allows you to extend CurseForge Launcher functionality, manipulate the DOM, hook runtime methods, and persist configuration.
---
## Plugin folder
## Plugin Directory Structure
Plugins live in the BetterCurseForge user directory:
Plugins are stored in the BetterCurseForge user directory:
```text
%APPDATA%\CurseForge\BetterCurseForge\plugins\
```
Each plugin gets its own subfolder:
Each plugin lives in its own dedicated subfolder:
```text
plugins/
@ -22,13 +22,13 @@ plugins/
└── styles.css # Optional: Companion stylesheet
```
The **Open Plugins Folder** button in the desktop app opens it.
You can open the plugins folder directly from the BetterCurseForge desktop GUI by clicking **Open Plugins Folder**.
---
## Manifest (`manifest.json`)
## Plugin Manifest (`manifest.json`)
`manifest.json` describes the plugin:
The `manifest.json` file declares your plugin's metadata:
```json
{
@ -42,22 +42,22 @@ The **Open Plugins Folder** button in the desktop app opens it.
}
```
### Fields
### Manifest Fields
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `name` | `string` | **Yes** | Name shown in the management UI. |
| `version` | `string` | **Yes** | Semantic version (e.g. `1.0.0`). |
| `description` | `string` | No | Short summary of what the plugin does. |
| `name` | `string` | **Yes** | Display name shown in the management UI. |
| `version` | `string` | **Yes** | Semantic version string (e.g. `1.0.0`). |
| `description` | `string` | No | Brief summary of what the plugin accomplishes. |
| `author` | `string` | No | Author name or handle. |
| `main` | `string` | No | Entry script. Defaults to `index.js`. |
| `styles` | `string` | No | Stylesheet that's injected automatically when the plugin is enabled. Defaults to `styles.css`. |
| `main` | `string` | No | Entry JavaScript file. Defaults to `index.js`. |
| `styles` | `string` | No | Companion stylesheet injected automatically on enable. Defaults to `styles.css`. |
---
## Lifecycle
## Plugin Lifecycle & Execution
A plugin's script runs when the plugin is enabled. It gets the `BetterCurseForge` API and a `plugin` context object:
Plugin scripts are executed when enabled, receiving the `BetterCurseForge` global API and a `plugin` context object:
```javascript
// index.js
@ -73,16 +73,16 @@ plugin.onDisable = () => {
};
```
::: tip STYLESHEETS
If your plugin folder has a `styles.css` (or the file named in `"styles"` in `manifest.json`), BetterCurseForge injects it into `<head>` when the plugin is enabled and removes it when the plugin is disabled. You don't need to inject or clean it up yourself.
::: tip AUTOMATIC STYLE INJECTION & REMOVAL
If your plugin folder contains a `styles.css` (or file declared in `"styles"` in `manifest.json`), BetterCurseForge automatically injects it into `<head>` when your plugin is enabled and removes it when disabled! You do not need to manually inject or clean up companion stylesheets.
:::
---
## Plugin API
## BetterCurseForge Plugin APIs
### 1. `BetterCurseForge.injectCSS(css)`
Injects CSS into `<head>`. Returns a function that removes the style tag:
Injects custom CSS dynamically into `<head>`. Returns a cleanup function that unmounts the style tag:
```javascript
const removeStyles = BetterCurseForge.injectCSS(`
@ -98,7 +98,7 @@ plugin.onDisable = () => {
---
### 2. `BetterCurseForge.settings`
Reads and writes settings that are saved to disk (`settings.json`):
Stores and retrieves persistent settings saved to disk (`settings.json`):
```javascript
// Retrieve a stored setting (or null if not set)
@ -111,10 +111,10 @@ await BetterCurseForge.settings.set('compactMode.density', 'ultra');
---
### 3. `BetterCurseForge.patcher`
Helpers for monkey-patching methods on objects. Each call returns a function that undoes the patch:
Safe method monkey-patching for objects and functions:
#### `patcher.before(object, methodName, callback)`
Runs your callback before the original method. The callback gets `(args)`:
Runs your callback before the target method executes. Receives `(args)`:
```javascript
const unpatch = BetterCurseForge.patcher.before(console, 'error', (args) => {
@ -125,7 +125,7 @@ plugin.onDisable = () => unpatch();
```
#### `patcher.after(object, methodName, callback)`
Runs after the original method. The callback gets `(args, result)` and can return a new value to replace the result:
Runs after the target method completes. Receives `(args, result)`. You can return a modified value:
```javascript
const unpatch = BetterCurseForge.patcher.after(targetObj, 'getItemTitle', (args, result) => {
@ -136,7 +136,7 @@ plugin.onDisable = () => unpatch();
```
#### `patcher.instead(object, methodName, callback)`
Replaces the original method. The callback gets `(args, originalMethod)`:
Completely supersedes the target method. Receives `(args, originalMethod)`:
```javascript
const unpatch = BetterCurseForge.patcher.instead(targetObj, 'fetchData', (args, originalFn) => {
@ -150,7 +150,7 @@ plugin.onDisable = () => unpatch();
---
### 4. `BetterCurseForge.on(event, callback)` / `.off()` / `.emit()`
An event emitter shared across the app:
Application-wide event emitter:
```javascript
const onReady = () => console.log('CurseForge UI is fully loaded!');
@ -164,9 +164,9 @@ plugin.onDisable = () => {
---
## Example: Compact Mode
## Complete Example: Compact Mode Plugin
This plugin makes CurseForge's layout denser.
This plugin adds a high-density layout mode to CurseForge.
::: code-group

View file

@ -1,33 +1,33 @@
# Themes
# Custom Themes & Theming Guide
BetterCurseForge can load custom themes and reload them live. A theme is a CSS file, optionally with a JSON manifest, and it appears in CurseForge's own theme settings.
BetterCurseForge features a native, live-reloading theming engine. Themes allow you to completely restyle the CurseForge Launcher using standard CSS and theme manifests, fully integrated into the official user interface.
---
## Picking a theme
## In-App Theme Selector
Client themes are added to CurseForge's own **Settings -> Themes** page.
BetterCurseForge embeds client themes directly into CurseForge's native **Settings -> Themes** menu.
### Finding client themes
1. Open CurseForge.
### How to Access Client Themes
1. Open CurseForge Launcher.
2. Click the **Settings** gear icon in the bottom-left corner.
3. Choose **Themes** in the settings sidebar.
4. Scroll past the *Classic*, *Colorblind* and *Premium* categories to reach **Client Themes**:
3. Select **Themes** from the settings sidebar.
4. Scroll down past the *Classic*, *Colorblind*, and *Premium* categories to find the **Client Themes** section:
![Settings Themes View](/logo.svg){width=48 style="display:block;margin:1rem auto;"}
### What the theme page does
- **Hover preview**: Hover over a theme card to preview it across the whole launcher. Move the cursor away to revert, or click to apply it.
- **One theme at a time**: Picking a client theme deselects any native skin, and the other way around.
- **Open Folder button**: Opens your themes folder in Explorer so you can drop in new stylesheets.
- **Refresh button**: Reloads your custom themes from disk, no restart needed.
- **Premium themes unlocked**: The official Premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge* and *Spookyforge*) are unlocked and free to use.
### Native Features in Settings -> Themes
- **Live Hover Preview**: Hovering your cursor over any theme card immediately previews the theme across the entire launcher in real time. Move your cursor away to revert, or click to apply.
- **Mutual Exclusivity**: Selecting a client theme seamlessly deselects native skins, and vice versa. Only one theme is active at a time.
- **"Open Folder" Button**: Directly opens your themes folder in Windows Explorer so you can drop in new stylesheets.
- **"Refresh" Button**: Reloads all installed custom themes from disk instantly without needing to restart CurseForge.
- **Native Premium Themes Unlocked**: All official CurseForge Premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge*, and *Spookyforge*) are permanently unlocked and free to use!
---
## Theme folder
## Theme Folder Location
Themes live in your user application data directory:
Themes are stored in your user application data directory:
```text
%APPDATA%\CurseForge\BetterCurseForge\themes\
@ -38,16 +38,16 @@ Full path:
C:\Users\<YourUsername>\AppData\Roaming\CurseForge\BetterCurseForge\themes\
```
The **Open Folder** button in **Settings -> Themes** opens it for you.
You can open this folder anytime by clicking the **Open Folder** button in **Settings -> Themes**.
---
## Theme layout
## Theme File Structure
There are two ways to lay out a theme:
BetterCurseForge supports two convenient formats:
### 1. Single file (recommended)
Put a `.css` file in `themes/`, with an optional `.json` manifest next to it:
### 1. Single-File Theme (Recommended)
Place a `.css` file in the `themes/` directory, with an optional accompanying `.json` manifest:
```text
themes/
@ -55,8 +55,8 @@ themes/
└── purple-night.json (optional metadata & preview colors)
```
### 2. Folder
Make a folder inside `themes/` containing `theme.css` and `theme.json`:
### 2. Folder-Based Theme
Create a folder inside `themes/` with a `theme.css` and `theme.json`:
```text
themes/
@ -68,9 +68,9 @@ themes/
---
## Manifest (`theme.json`)
## Theme Manifest Format (`theme.json`)
The manifest holds the theme's metadata and the colors of the small preview card shown in **Settings -> Themes**:
A `.json` manifest provides metadata and defines the miniature preview card colors shown in the **Settings -> Themes** menu:
```json
{
@ -85,78 +85,78 @@ The manifest holds the theme's metadata and the colors of the small preview card
}
```
### Fields
### Manifest Fields
| Field | Type | Description |
| :--- | :--- | :--- |
| `name` | `string` | Name shown on the theme card and in settings. |
| `version` | `string` | Semantic version (e.g. `1.0.0`). |
| `description` | `string` | Short description, shown as a tooltip when you hover the title. |
| `name` | `string` | Display name shown on the theme card and in settings. |
| `version` | `string` | Semantic version string (e.g. `1.0.0`). |
| `description` | `string` | Short tooltip description displayed when hovering the title. |
| `author` | `string` | Author name or handle. |
| `primaryColor` | `string` | Hex color of the square icon on the preview card. |
| `titleColor` | `string` | Hex color of the top bar on the preview card. |
| `bodyColor` | `string` | Hex color of the lower text bar on the preview card. |
| `backgroundColor`| `string` | Hex background color of the preview card. |
| `primaryColor` | `string` | Hex color for the primary square icon in the thumbnail card. |
| `titleColor` | `string` | Hex color for the top bar in the thumbnail card. |
| `bodyColor` | `string` | Hex color for the lower text bar in the thumbnail card. |
| `backgroundColor`| `string` | Hex background color for the thumbnail card itself. |
---
## CSS variables
## CurseForge CSS Variables Reference
CurseForge is styled with CSS custom properties. Overriding them lets you theme most of the app without touching individual class names.
CurseForge is styled using CSS Custom Properties (variables). Overriding these variables allows you to theme 95% of the application cleanly without touching individual class names.
### Brand & accents
### Brand & Accents
| Variable | Description |
| :--- | :--- |
| `--surface-brand-brand` | Main brand accent: active tabs, primary buttons, indicators. |
| `--surface-brand-brand` | Primary brand accent (active tabs, primary buttons, indicators). |
| `--surface-brand-brand-hover` | Hover state for brand-colored elements. |
| `--color-primary` | Main accent color. |
| `--color-accent` | Secondary highlight color. |
| `--selected-item-border` | Border style for selected items and active inputs. |
### Surfaces & backgrounds
### Surfaces & Backgrounds
| Variable | Description |
| :--- | :--- |
| `--color-background` | Main app background (content views, modpack library). |
| `--color-background` | Main application background (content views, modpack library). |
| `--surface-base` | Darkest background layer (scroll tracks, container bases). |
| `--surface-primary` | Raised surface (sidebars, sub-panels). |
| `--surface-secondary` | Second surface layer (navigation menus, dialog panels). |
| `--surface-tertiery` | Third surface layer (mod cards, list row containers). |
| `--surface-on-surface` | Faint translucent overlay for buttons (`rgba(...)`). |
| `--surface-primary` | Elevated primary surface (sidebars, sub-panels). |
| `--surface-secondary` | Secondary surface layer (navigation menus, dialog panels). |
| `--surface-tertiery` | Tertiary surface layer (mod cards, list row containers). |
| `--surface-on-surface` | Subtle translucent overlay for buttons (`rgba(...)`). |
| `--surface-on-surface-hover` | Translucent hover overlay for buttons. |
### Modpack & library cards
### Modpack & Library Cards
| Variable | Description |
| :--- | :--- |
| `--surface-card` | Base background of modpack and project cards. |
| `--surface-card-hover` | Background when hovering over a modpack card. |
| `--color-surface-card-active` | Active or clicked state of a card. |
| `--surface-card` | Modpack / project card base background. |
| `--surface-card-hover` | Background color when hovering over modpack cards. |
| `--color-surface-card-active` | Active or clicked state for card items. |
### Typography & text
### Typography & Text
| Variable | Description |
| :--- | :--- |
| `--text-pure` | Pure white text (`#ffffff`). |
| `--text-primary` | Main readable text color. |
| `--text-primary` | Primary readable text color. |
| `--color-text-title` | Headings, modpack titles, section names. |
| `--color-text-body` | Normal body and description text. |
| `--color-text-body` | Standard body and description text. |
| `--color-text-secondary` | Subtitles, authors, version numbers. |
### Title bar & navigation
### Title Bar & Navigation
| Variable | Description |
| :--- | :--- |
| `--title-bar-background` | Background color or gradient of the window title bar. |
| `--btn-window-action` | Icons on the minimize, maximize and close buttons. |
| `--color-scrollbar-thumb` | Scrollbar thumb color. |
| `--title-bar-background` | Background color or gradient for the top window title bar. |
| `--btn-window-action` | Minimize, maximize, and close button icons. |
| `--color-scrollbar-thumb` | Scrollbar thumb handle color. |
---
## Example: Emerald Night
## Example Theme: Emerald Night
A full working theme. Save the stylesheet as `emerald-night.css` and the manifest as `emerald-night.json`:
Here is a complete, working theme stylesheet you can save as `emerald-night.css`:
::: code-group
@ -237,12 +237,12 @@ button[class*="primary"]:not([class*="secondary"]):hover {
---
## Inspecting elements
## Tips for Inspecting Elements
CurseForge is an Electron and React app, so you can inspect its UI with Chromium DevTools:
Because CurseForge runs on Electron and React, you can inspect any UI element using Chromium DevTools:
1. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd> in CurseForge to open DevTools.
2. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd> to turn on the element inspector.
3. Click a button, banner, header or list item to see its computed styles and classes in the **Elements** panel.
4. Try CSS changes in the **Styles** tab before adding them to your theme.
5. In **Settings -> Themes**, click **Refresh** to load your updated stylesheet.
2. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd> to activate the element inspector.
3. Click on any button, banner, header, or list item to reveal its computed styles and classes in the **Elements** panel.
4. Test CSS changes live in the **Styles** tab before adding them to your theme file.
5. In **Settings -> Themes**, click **Refresh** to instantly apply your modified theme stylesheet!

View file

@ -3,8 +3,8 @@ layout: home
hero:
name: "BetterCurseForge"
text: "A client mod for the CurseForge launcher"
tagline: "Ad blocking, custom themes, unlocked premium skins, and plugins."
text: "Client mod for CurseForge Launcher"
tagline: "Ad-free experience, custom themes, unlocked premium skins, and plugins."
image:
src: /logo.png
alt: BetterCurseForge
@ -18,30 +18,30 @@ hero:
features:
- icon: 🚫
title: Ad blocking
details: Ad requests are blocked at the network level, and CSS rules collapse the video ads, banners and empty containers that are left. The elements stay in the DOM, so React keeps working.
title: Complete Ad Removal
details: Multi-layer elimination (zero-ad CSS rules, network-level request blocking, and layout collapsing) removes all video ads, banners, and empty containers without disrupting React reconciliation.
- icon: 🎨
title: Themes and free skins
details: Client themes show up in Settings -> Themes with live hover previews and a shortcut to the themes folder. The official premium skins are unlocked too.
title: Client Themes & Free Skins
details: Integrated directly into Settings -> Themes with live hover previews, theme folder shortcuts, and all native CurseForge Premium themes unlocked for free.
- icon: 🔄
title: Auto-repatch after updates
details: When the launcher updates, BetterCurseForge stops the unpatched copy from opening, shows a small splash window with progress, and repatches in a few seconds.
title: Auto-Repatch & Smooth Updates
details: Survives launcher updates effortlessly. The active interceptor suppresses unpatched windows, and a sleek splash GUI displays live progress while repatching in seconds.
- icon: 🖥️
title: GUI and CLI
details: A dark-themed desktop app with one-click actions, plus a command-line interface for scripting.
title: Modern GUI & CLI
details: Includes an intuitive dark-themed desktop application with one-click actions, as well as a full command-line interface for power users.
- icon: 🧩
title: Plugins
details: Write small JavaScript plugins that patch methods at runtime and keep their settings between restarts.
title: Plugin System
details: Extend launcher capabilities with lightweight JavaScript plugins using our runtime monkey-patcher and persistent settings API.
- icon: 🛡️
title: Backups and unpatching
details: Original files are backed up first (`app.asar.vanilla` and `CurseForge.exe.vanilla`), and you can restore stock CurseForge with one click.
title: Safe & 100% Reversible
details: Automatically creates clean vanilla backups (`app.asar.vanilla` and `CurseForge.exe.vanilla`). You can restore stock CurseForge at any time with a single click.
---
<div class="quickstart-section" style="margin-top: 3rem; text-align: center;">
## Quick Start
Download `BetterCurseForge.exe`, then open it for the GUI or patch from a terminal:
Download `BetterCurseForge.exe` and patch in one click:
```bash
# Launch the desktop GUI:
@ -57,15 +57,14 @@ BetterCurseForge.exe patch
## Why BetterCurseForge?
CurseForge hosts Minecraft modpacks, World of Warcraft addons and other game mods, but the desktop app comes with ad containers, premium-only themes and telemetry.
CurseForge is the premier platform for Minecraft modpacks, World of Warcraft addons, and game mods, but the official desktop app is burdened with intrusive ad containers, locked premium themes, and heavy telemetry.
BetterCurseForge changes that:
**BetterCurseForge** restores a clean, fast, and customizable experience:
- **Native theme picker**: Client themes appear in **Settings -> Themes** next to the official ones, with live hover previews.
- **Premium themes unlocked**: The official premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Spookyforge*) work without a subscription.
- **No ad columns**: Ad columns and sidebars are collapsed, so modpack grids use the full width of the window.
- **Survives updates**: When CurseForge updates, BetterCurseForge detects it and reapplies the patch in a few seconds.
- **Easy to undo**: Unpatch from the GUI or with `BetterCurseForge.exe unpatch` to get vanilla CurseForge back.
- **Seamless Native UI**: Client themes appear natively under **Settings -> Themes** alongside official themes, complete with real-time live hover previews.
- **Unlocked Premium Themes**: Access all native CurseForge Premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Spookyforge*) without a subscription.
- **Zero-Ad Layouts**: Ad columns and sidebars are eliminated from the layout, letting modpack grids expand to fill your entire display.
- **Automated Update Resistance**: Never worry about CurseForge updates breaking your setup—**Auto-Repatch** automatically detects updates and re-applies the patch in seconds.
- **One-Click Reversible**: Easily unpatch anytime via the GUI or `BetterCurseForge.exe unpatch` to restore vanilla CurseForge.
</div>