Compare commits
2 commits
c66cd9484a
...
fed62b18bc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fed62b18bc | ||
|
|
862ed0f7d6 |
8 changed files with 205 additions and 201 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -41,4 +41,6 @@ desktop.ini
|
||||||
tools/
|
tools/
|
||||||
update_icon.bat
|
update_icon.bat
|
||||||
update_icon.py
|
update_icon.py
|
||||||
|
docs_for_claude.json
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -90,7 +90,7 @@ and `themes/my-theme.css`:
|
||||||
|
|
||||||
### Writing a plugin
|
### Writing a plugin
|
||||||
|
|
||||||
A plugin lives in its own subfolder in `plugins/<plugin-name>/` with a `manifest.json`, an `index.js`, and an optional companion `styles.css`.
|
A plugin lives in its own subfolder, `plugins/<plugin-name>/`, with a `manifest.json`, an `index.js`, and optionally a `styles.css`.
|
||||||
|
|
||||||
`plugins/my-plugin/manifest.json`:
|
`plugins/my-plugin/manifest.json`:
|
||||||
|
|
||||||
|
|
@ -121,10 +121,10 @@ plugin.onDisable = () => {
|
||||||
|
|
||||||
#### What plugins can do
|
#### What plugins can do
|
||||||
|
|
||||||
- **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.
|
- **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.** Intercept, modify, or override internal JavaScript functions with `BetterCurseForge.patcher.before()`, `after()`, or `instead()`.
|
- **Method patching.** Hook into or replace 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)`.
|
- **Persistent settings.** Save and read preferences across restarts with `BetterCurseForge.settings.get(key)` and `set(key, value)`.
|
||||||
- **Event listening.** Hook into application lifecycle events using `BetterCurseForge.on(event, callback)`.
|
- **Events.** Listen for app lifecycle events with `BetterCurseForge.on(event, callback)`.
|
||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ The executable ends up in `dist/BetterCurseForge.exe`.
|
||||||
|
|
||||||
### Syncing changes from GitHub
|
### Syncing changes from GitHub
|
||||||
|
|
||||||
To pull down updates made on GitHub:
|
To pull changes from GitHub:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
git pull origin master
|
git pull origin master
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ import { defineConfig } from 'vitepress';
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
title: 'BetterCurseForge',
|
title: 'BetterCurseForge',
|
||||||
description: 'Client mod for CurseForge Launcher',
|
description: 'Client mod for CurseForge Launcher',
|
||||||
base: process.env.BASE_URL || '/BetterCurseForge/',
|
base: process.env.BASE_URL || '/',
|
||||||
appearance: 'dark',
|
appearance: 'dark',
|
||||||
lastUpdated: true,
|
lastUpdated: true,
|
||||||
cleanUrls: true,
|
cleanUrls: true,
|
||||||
|
|
||||||
head: [
|
head: [
|
||||||
['link', { rel: 'icon', type: 'image/png', href: (process.env.BASE_URL || '/BetterCurseForge/') + 'logo.png' }],
|
['link', { rel: 'icon', type: 'image/png', href: (process.env.BASE_URL || '/') + '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' }],
|
||||||
|
|
@ -23,6 +23,7 @@ export default defineConfig({
|
||||||
nav: [
|
nav: [
|
||||||
{ text: 'Guide', link: '/guide/installation' },
|
{ text: 'Guide', link: '/guide/installation' },
|
||||||
{ text: 'API', link: '/api/' },
|
{ text: 'API', link: '/api/' },
|
||||||
|
{ text: 'Forgejo', link: 'http://92.5.113.44:3000/tomasalias/BetterCurseForge' },
|
||||||
{ text: 'GitHub', link: 'https://github.com/tomasalias/BetterCurseForge' },
|
{ text: 'GitHub', link: 'https://github.com/tomasalias/BetterCurseForge' },
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# API Reference
|
# API Reference
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## TypeScript Definition
|
## TypeScript definitions
|
||||||
|
|
||||||
For TypeScript development and autocompletion:
|
If you're using TypeScript, this gives you types and autocompletion:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
declare global {
|
declare global {
|
||||||
|
|
@ -54,7 +54,7 @@ export interface BetterCurseForgeAPI {
|
||||||
### `BetterCurseForge.version`
|
### `BetterCurseForge.version`
|
||||||
|
|
||||||
- **Type**: `string`
|
- **Type**: `string`
|
||||||
- **Description**: The currently running BetterCurseForge runtime version.
|
- **Description**: Version of the BetterCurseForge runtime that's currently running.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
console.log(BetterCurseForge.version); // "1.0.0"
|
console.log(BetterCurseForge.version); // "1.0.0"
|
||||||
|
|
@ -64,11 +64,11 @@ console.log(BetterCurseForge.version); // "1.0.0"
|
||||||
|
|
||||||
### `BetterCurseForge.injectCSS(css)`
|
### `BetterCurseForge.injectCSS(css)`
|
||||||
|
|
||||||
Dynamically injects CSS rules into the document `<head>`.
|
Adds CSS rules to the document `<head>`.
|
||||||
|
|
||||||
- **Parameters**:
|
- **Parameters**:
|
||||||
- `css` *(string)*: Raw CSS string to inject.
|
- `css` *(string)*: The CSS to inject.
|
||||||
- **Returns**: `() => void` — Function that removes the injected `<style>` tag when called.
|
- **Returns**: `() => void`. Call it to remove the injected `<style>` tag.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Inject styles
|
// Inject styles
|
||||||
|
|
@ -86,15 +86,15 @@ uninject();
|
||||||
|
|
||||||
## Settings API (`BetterCurseForge.settings`)
|
## Settings API (`BetterCurseForge.settings`)
|
||||||
|
|
||||||
Manages persistent configuration stored in `%APPDATA%\CurseForge\BetterCurseForge\settings.json`.
|
Persistent configuration, stored in `%APPDATA%\CurseForge\BetterCurseForge\settings.json`.
|
||||||
|
|
||||||
### `settings.get(key?)`
|
### `settings.get(key?)`
|
||||||
|
|
||||||
Retrieves a setting by key, or returns the entire settings object if no key is specified.
|
Returns the setting for `key`, or the whole settings object if you don't pass a key.
|
||||||
|
|
||||||
- **Parameters**:
|
- **Parameters**:
|
||||||
- `key` *(string, optional)*: Setting key name.
|
- `key` *(string, optional)*: Name of the setting.
|
||||||
- **Returns**: `Promise<any>` — Value of the setting, or `null`.
|
- **Returns**: `Promise<any>`. The value, or `null` if it isn't set.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Retrieve a specific value
|
// Retrieve a specific value
|
||||||
|
|
@ -106,12 +106,12 @@ const allSettings = await BetterCurseForge.settings.get();
|
||||||
|
|
||||||
### `settings.set(key, value)`
|
### `settings.set(key, value)`
|
||||||
|
|
||||||
Stores a setting value and writes it asynchronously to disk.
|
Saves a value and writes it to disk asynchronously.
|
||||||
|
|
||||||
- **Parameters**:
|
- **Parameters**:
|
||||||
- `key` *(string)*: Setting key name.
|
- `key` *(string)*: Name of the setting.
|
||||||
- `value` *(any)*: JSON-serializable value.
|
- `value` *(any)*: Any JSON-serializable value.
|
||||||
- **Returns**: `Promise<boolean>` — `true` if saved successfully.
|
- **Returns**: `Promise<boolean>`. `true` if the save worked.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
await BetterCurseForge.settings.set('myPlugin.fontSize', 14);
|
await BetterCurseForge.settings.set('myPlugin.fontSize', 14);
|
||||||
|
|
@ -121,17 +121,17 @@ await BetterCurseForge.settings.set('myPlugin.fontSize', 14);
|
||||||
|
|
||||||
## Patcher API (`BetterCurseForge.patcher`)
|
## Patcher API (`BetterCurseForge.patcher`)
|
||||||
|
|
||||||
Provides monkey-patching utilities to inspect, modify, or replace methods on JavaScript objects and modules safely.
|
Utilities for inspecting, modifying or replacing methods on JavaScript objects and modules. Every function returns an unpatch function.
|
||||||
|
|
||||||
### `patcher.before(obj, method, callback)`
|
### `patcher.before(obj, method, callback)`
|
||||||
|
|
||||||
Executes `callback(args)` **before** the target method is invoked.
|
Runs `callback(args)` **before** the target method is called.
|
||||||
|
|
||||||
- **Parameters**:
|
- **Parameters**:
|
||||||
- `obj` *(object)*: Target object.
|
- `obj` *(object)*: Target object.
|
||||||
- `method` *(string)*: Method name on `obj`.
|
- `method` *(string)*: Method name on `obj`.
|
||||||
- `callback` *(Function)*: Function receiving arguments: `(args) => void`.
|
- `callback` *(Function)*: `(args) => void`.
|
||||||
- **Returns**: `() => void` — Unpatch function that restores the original method.
|
- **Returns**: `() => void`. Call it to restore the original method.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const unpatch = BetterCurseForge.patcher.before(console, 'warn', (args) => {
|
const unpatch = BetterCurseForge.patcher.before(console, 'warn', (args) => {
|
||||||
|
|
@ -146,13 +146,13 @@ unpatch();
|
||||||
|
|
||||||
### `patcher.after(obj, method, callback)`
|
### `patcher.after(obj, method, callback)`
|
||||||
|
|
||||||
Executes `callback(args, result)` **after** the target method runs. You can return a new value to replace the method's return value.
|
Runs `callback(args, result)` **after** the target method. Return a value to replace the method's return value.
|
||||||
|
|
||||||
- **Parameters**:
|
- **Parameters**:
|
||||||
- `obj` *(object)*: Target object.
|
- `obj` *(object)*: Target object.
|
||||||
- `method` *(string)*: Method name on `obj`.
|
- `method` *(string)*: Method name on `obj`.
|
||||||
- `callback` *(Function)*: `(args, result) => any`.
|
- `callback` *(Function)*: `(args, result) => any`.
|
||||||
- **Returns**: `() => void` — Unpatch function.
|
- **Returns**: `() => void`. Unpatch function.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const unpatch = BetterCurseForge.patcher.after(service, 'getTitle', (args, result) => {
|
const unpatch = BetterCurseForge.patcher.after(service, 'getTitle', (args, result) => {
|
||||||
|
|
@ -166,13 +166,13 @@ unpatch();
|
||||||
|
|
||||||
### `patcher.instead(obj, method, callback)`
|
### `patcher.instead(obj, method, callback)`
|
||||||
|
|
||||||
Completely overrides the target method. Receives both the incoming arguments and a reference to the original method.
|
Replaces the target method. The callback gets the arguments and a reference to the original method.
|
||||||
|
|
||||||
- **Parameters**:
|
- **Parameters**:
|
||||||
- `obj` *(object)*: Target object.
|
- `obj` *(object)*: Target object.
|
||||||
- `method` *(string)*: Method name on `obj`.
|
- `method` *(string)*: Method name on `obj`.
|
||||||
- `callback` *(Function)*: `(args, originalFn) => any`.
|
- `callback` *(Function)*: `(args, originalFn) => any`.
|
||||||
- **Returns**: `() => void` — Unpatch function.
|
- **Returns**: `() => void`. Unpatch function.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const unpatch = BetterCurseForge.patcher.instead(target, 'openExternalUrl', (args, originalFn) => {
|
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 includes an event bus for inter-plugin and lifecycle communication.
|
BetterCurseForge has an event bus that plugins can use to talk to each other and to react to lifecycle events.
|
||||||
|
|
||||||
### `BetterCurseForge.on(event, callback)`
|
### `BetterCurseForge.on(event, callback)`
|
||||||
Subscribes a listener to an event.
|
Adds a listener for an event.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const onReady = () => console.log('BetterCurseForge is ready!');
|
const onReady = () => console.log('BetterCurseForge is ready!');
|
||||||
|
|
@ -201,14 +201,14 @@ BetterCurseForge.on('ready', onReady);
|
||||||
```
|
```
|
||||||
|
|
||||||
### `BetterCurseForge.off(event, callback)`
|
### `BetterCurseForge.off(event, callback)`
|
||||||
Unsubscribes a listener.
|
Removes a listener.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
BetterCurseForge.off('ready', onReady);
|
BetterCurseForge.off('ready', onReady);
|
||||||
```
|
```
|
||||||
|
|
||||||
### `BetterCurseForge.emit(event, ...args)`
|
### `BetterCurseForge.emit(event, ...args)`
|
||||||
Emits an event to all registered listeners.
|
Calls every listener registered for the event.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
BetterCurseForge.emit('custom-event', { detail: 123 });
|
BetterCurseForge.emit('custom-event', { detail: 123 });
|
||||||
|
|
@ -216,10 +216,10 @@ BetterCurseForge.emit('custom-event', { detail: 123 });
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Subsystem Managers
|
## Subsystem managers
|
||||||
|
|
||||||
### `BetterCurseForge.themes` (`ThemeManager`)
|
### `BetterCurseForge.themes` (`ThemeManager`)
|
||||||
Provides programmatic control over custom themes:
|
Control custom themes from code:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Get all discovered themes
|
// Get all discovered themes
|
||||||
|
|
@ -242,7 +242,7 @@ BetterCurseForge.themes.removeTheme(true);
|
||||||
```
|
```
|
||||||
|
|
||||||
### `BetterCurseForge.plugins` (`PluginManager`)
|
### `BetterCurseForge.plugins` (`PluginManager`)
|
||||||
Provides programmatic control over plugins:
|
Control plugins from code:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Get all installed plugins
|
// Get all installed plugins
|
||||||
|
|
@ -253,7 +253,7 @@ await BetterCurseForge.plugins.togglePlugin('compact-mode');
|
||||||
```
|
```
|
||||||
|
|
||||||
### `BetterCurseForge.ads` (`AdBlocker`)
|
### `BetterCurseForge.ads` (`AdBlocker`)
|
||||||
Internal ad blocker runtime:
|
The internal ad blocker runtime:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Contains zero-ad CSS rules and ad container cleanup handlers
|
// Contains zero-ad CSS rules and ad container cleanup handlers
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,51 @@
|
||||||
# Installation & Usage
|
# Installation & Usage
|
||||||
|
|
||||||
This guide covers installing, managing, and uninstalling **BetterCurseForge** using either the desktop GUI application or the command-line interface.
|
How to install, check, and remove **BetterCurseForge**, using either the desktop app or the command line.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Before using BetterCurseForge, make sure you have:
|
You need:
|
||||||
|
|
||||||
- **Windows 10 or Windows 11** (64-bit)
|
- **Windows 10 or Windows 11** (64-bit)
|
||||||
- **CurseForge Launcher** installed (standalone or Overwolf edition)
|
- **CurseForge Launcher** installed (standalone or Overwolf edition)
|
||||||
- CurseForge closed before running the patcher
|
- CurseForge closed when you run the patcher
|
||||||
|
|
||||||
::: tip SAFE & 100% REVERSIBLE
|
::: tip BACKUPS
|
||||||
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.
|
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.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Method 1: Using the Desktop GUI (Recommended)
|
## Option 1: Desktop GUI (recommended)
|
||||||
|
|
||||||
BetterCurseForge comes as a standalone desktop executable with a sleek, dark-themed graphical interface.
|
BetterCurseForge is a single executable with a dark-themed GUI.
|
||||||
|
|
||||||
### Step 1: Download `BetterCurseForge.exe`
|
### Step 1: Download `BetterCurseForge.exe`
|
||||||
|
|
||||||
Download the latest executable from the official releases:
|
Grab the latest build from the releases page:
|
||||||
|
|
||||||
- [Download `BetterCurseForge.exe`](https://github.com/tomasalias/BetterCurseForge/releases/latest)
|
- [Download `BetterCurseForge.exe`](https://github.com/tomasalias/BetterCurseForge/releases/latest)
|
||||||
|
|
||||||
Place it in any convenient folder (e.g., Downloads, Tools, or desktop).
|
Put it anywhere you like (Downloads, a tools folder, the desktop).
|
||||||
|
|
||||||
### Step 2: Open BetterCurseForge
|
### Step 2: Open BetterCurseForge
|
||||||
|
|
||||||
Double-click `BetterCurseForge.exe` to launch the application.
|
Double-click `BetterCurseForge.exe` to open it.
|
||||||
|
|
||||||
{width=64 style="display:block;margin:1rem auto;"}
|
{width=64 style="display:block;margin:1rem auto;"}
|
||||||
|
|
||||||
The application automatically:
|
On startup it:
|
||||||
- Detects your CurseForge installation folder from the Windows Registry.
|
- Finds your CurseForge install folder through the Windows Registry.
|
||||||
- Checks whether your client is currently vanilla or patched.
|
- Checks whether CurseForge is vanilla or patched.
|
||||||
- Displays the installed CurseForge version and installation directory.
|
- Shows the installed CurseForge version and install directory.
|
||||||
|
|
||||||
### Step 3: Click "Patch CurseForge"
|
### Step 3: Click "Patch CurseForge"
|
||||||
|
|
||||||
1. Click the **Patch CurseForge** button.
|
1. Click the **Patch CurseForge** button.
|
||||||
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.
|
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. You will see live output in the log panel confirming:
|
3. The log panel shows progress as it goes:
|
||||||
```text
|
```text
|
||||||
[+] Found CurseForge at: C:\Users\Username\AppData\Local\Programs\CurseForge Windows
|
[+] Found CurseForge at: C:\Users\Username\AppData\Local\Programs\CurseForge Windows
|
||||||
[+] Unlocked native premium themes (2 route checks)
|
[+] Unlocked native premium themes (2 route checks)
|
||||||
|
|
@ -60,17 +60,17 @@ The application automatically:
|
||||||
[+] Auto-repatch watcher active and persistent across updates.
|
[+] Auto-repatch watcher active and persistent across updates.
|
||||||
[*] CurseForge successfully patched! Ads removed, themes & plugins enabled.
|
[*] CurseForge successfully patched! Ads removed, themes & plugins enabled.
|
||||||
```
|
```
|
||||||
4. Click **Launch CurseForge** or open your usual desktop shortcut—you're done!
|
4. Click **Launch CurseForge** or start it from your usual shortcut. That's it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Method 2: Command-Line Interface (CLI)
|
## Option 2: Command line
|
||||||
|
|
||||||
For automated workflows, scripting, or headless environments, `BetterCurseForge.exe` provides a comprehensive CLI:
|
If you want to script it or run it without the GUI, `BetterCurseForge.exe` has a CLI.
|
||||||
|
|
||||||
### Patching via Terminal
|
### Patching
|
||||||
|
|
||||||
Open PowerShell or Command Prompt in the folder containing `BetterCurseForge.exe`:
|
Open PowerShell or Command Prompt in the folder with `BetterCurseForge.exe`:
|
||||||
|
|
||||||
::: code-group
|
::: code-group
|
||||||
|
|
||||||
|
|
@ -84,9 +84,9 @@ BetterCurseForge.exe patch
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Checking Status
|
### Checking status
|
||||||
|
|
||||||
Verify whether your client is patched:
|
See whether CurseForge is patched:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\BetterCurseForge.exe status
|
.\BetterCurseForge.exe status
|
||||||
|
|
@ -101,26 +101,26 @@ ASAR Hash: Verified
|
||||||
|
|
||||||
### Unpatching
|
### Unpatching
|
||||||
|
|
||||||
Restore your vanilla CurseForge client:
|
Go back to stock CurseForge:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\BetterCurseForge.exe unpatch
|
.\BetterCurseForge.exe unpatch
|
||||||
```
|
```
|
||||||
|
|
||||||
This restores `resources/app.asar` from the vanilla backup and re-verifies original executable signatures.
|
This restores `resources/app.asar` from the vanilla backup and re-verifies the original executable signatures.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Auto-Repatch: Surviving CurseForge Updates
|
## Auto-repatch after CurseForge updates
|
||||||
|
|
||||||
When the official CurseForge Launcher downloads an automatic update, it overwrites `app.asar` with an unpatched version and attempts to restart.
|
When CurseForge updates itself, it overwrites `app.asar` with a stock copy and restarts.
|
||||||
|
|
||||||
BetterCurseForge provides an autonomous, smooth transition with **zero screen flashing**:
|
BetterCurseForge handles this without the unpatched launcher ever showing up on screen.
|
||||||
|
|
||||||
### Managing Auto-Repatch in the GUI
|
### Managing it in the GUI
|
||||||
- In the BetterCurseForge desktop application, simply toggle the **Auto-Repatch on Update** button (turns green when active).
|
- Toggle the **Auto-Repatch on Update** button in the app. It turns green when it's on.
|
||||||
|
|
||||||
### Managing Auto-Repatch via CLI
|
### Managing it from the CLI
|
||||||
```powershell
|
```powershell
|
||||||
# Run the background watcher daemon directly:
|
# Run the background watcher daemon directly:
|
||||||
.\BetterCurseForge.exe watch
|
.\BetterCurseForge.exe watch
|
||||||
|
|
@ -133,18 +133,18 @@ BetterCurseForge provides an autonomous, smooth transition with **zero screen fl
|
||||||
.\BetterCurseForge.exe unpatch # Restores vanilla client and stops watcher
|
.\BetterCurseForge.exe unpatch # Restores vanilla client and stops watcher
|
||||||
```
|
```
|
||||||
|
|
||||||
### How Auto-Repatch Works
|
### How it works
|
||||||
1. **Background Watcher**: Runs silently with 0% CPU consumption via Windows directory event notifications and heartbeat checks.
|
1. **Background watcher**: Runs in the background on Windows directory change notifications and periodic heartbeat checks, so CPU use is negligible.
|
||||||
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.
|
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. **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.
|
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 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`.
|
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. **Seamless Relaunch**: The patched BetterCurseForge client opens automatically, and the splash window gracefully closes.
|
5. **Relaunch**: The patched CurseForge opens and the splash window closes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How It Works Under the Hood
|
## Under the hood
|
||||||
|
|
||||||
Modern versions of CurseForge include **ASAR Code Integrity Verification** enforced directly inside `CurseForge.exe`:
|
Recent CurseForge versions verify the integrity of `app.asar` from inside `CurseForge.exe`. This is the patch flow:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
|
|
@ -158,19 +158,19 @@ flowchart TD
|
||||||
H --> I["Launch Modded CurseForge with Full Permissions"]
|
H --> I["Launch Modded CurseForge with Full Permissions"]
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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. **Premium Skin Unlock**: Uses version-resilient AST/regex matching to unlock all official CurseForge skins (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Halloween*, etc.) in **Settings -> Themes**.
|
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 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.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Troubleshooting & FAQ
|
## Troubleshooting
|
||||||
|
|
||||||
::: details How do I verify BetterCurseForge is running?
|
::: details How do I tell it's working?
|
||||||
1. Open CurseForge.
|
1. Open CurseForge.
|
||||||
2. Notice the total absence of banner ads and video sponsor frames.
|
2. There should be no banner ads or 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.).
|
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 will display:
|
4. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd> to open DevTools. The console should print:
|
||||||
```text
|
```text
|
||||||
[BetterCF] Initializing renderer runtime...
|
[BetterCF] Initializing renderer runtime...
|
||||||
[BetterCF] Theme manager ready (1 theme(s) found). Active: purple-night.css
|
[BetterCF] Theme manager ready (1 theme(s) found). Active: purple-night.css
|
||||||
|
|
@ -178,10 +178,10 @@ flowchart TD
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: details Antivirus false-positive warning
|
::: details Antivirus flags the exe
|
||||||
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.
|
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 Permission denied / EPERM error
|
::: details Permission denied / EPERM error
|
||||||
If your CurseForge launcher is installed under `C:\Program Files`, modifying files requires Administrator permissions. Right-click `BetterCurseForge.exe` and select **Run as Administrator**.
|
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**.
|
||||||
:::
|
:::
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
# Writing Plugins
|
# Writing Plugins
|
||||||
|
|
||||||
BetterCurseForge features a lightweight plugin system that allows you to extend CurseForge Launcher functionality, manipulate the DOM, hook runtime methods, and persist configuration.
|
Plugins are small JavaScript modules that run inside CurseForge. They can change the DOM, patch methods at runtime, and save settings between sessions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Plugin Directory Structure
|
## Plugin folder
|
||||||
|
|
||||||
Plugins are stored in the BetterCurseForge user directory:
|
Plugins live in the BetterCurseForge user directory:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
%APPDATA%\CurseForge\BetterCurseForge\plugins\
|
%APPDATA%\CurseForge\BetterCurseForge\plugins\
|
||||||
```
|
```
|
||||||
|
|
||||||
Each plugin lives in its own dedicated subfolder:
|
Each plugin gets its own subfolder:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
plugins/
|
plugins/
|
||||||
|
|
@ -22,13 +22,13 @@ plugins/
|
||||||
└── styles.css # Optional: Companion stylesheet
|
└── styles.css # Optional: Companion stylesheet
|
||||||
```
|
```
|
||||||
|
|
||||||
You can open the plugins folder directly from the BetterCurseForge desktop GUI by clicking **Open Plugins Folder**.
|
The **Open Plugins Folder** button in the desktop app opens it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Plugin Manifest (`manifest.json`)
|
## Manifest (`manifest.json`)
|
||||||
|
|
||||||
The `manifest.json` file declares your plugin's metadata:
|
`manifest.json` describes the plugin:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -42,22 +42,22 @@ The `manifest.json` file declares your plugin's metadata:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manifest Fields
|
### Fields
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
| :--- | :--- | :--- | :--- |
|
| :--- | :--- | :--- | :--- |
|
||||||
| `name` | `string` | **Yes** | Display name shown in the management UI. |
|
| `name` | `string` | **Yes** | Name shown in the management UI. |
|
||||||
| `version` | `string` | **Yes** | Semantic version string (e.g. `1.0.0`). |
|
| `version` | `string` | **Yes** | Semantic version (e.g. `1.0.0`). |
|
||||||
| `description` | `string` | No | Brief summary of what the plugin accomplishes. |
|
| `description` | `string` | No | Short summary of what the plugin does. |
|
||||||
| `author` | `string` | No | Author name or handle. |
|
| `author` | `string` | No | Author name or handle. |
|
||||||
| `main` | `string` | No | Entry JavaScript file. Defaults to `index.js`. |
|
| `main` | `string` | No | Entry script. Defaults to `index.js`. |
|
||||||
| `styles` | `string` | No | Companion stylesheet injected automatically on enable. Defaults to `styles.css`. |
|
| `styles` | `string` | No | Stylesheet that's injected automatically when the plugin is enabled. Defaults to `styles.css`. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Plugin Lifecycle & Execution
|
## Lifecycle
|
||||||
|
|
||||||
Plugin scripts are executed when enabled, receiving the `BetterCurseForge` global API and a `plugin` context object:
|
A plugin's script runs when the plugin is enabled. It gets the `BetterCurseForge` API and a `plugin` context object:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// index.js
|
// index.js
|
||||||
|
|
@ -73,16 +73,16 @@ plugin.onDisable = () => {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
::: tip AUTOMATIC STYLE INJECTION & REMOVAL
|
::: tip STYLESHEETS
|
||||||
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.
|
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.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BetterCurseForge Plugin APIs
|
## Plugin API
|
||||||
|
|
||||||
### 1. `BetterCurseForge.injectCSS(css)`
|
### 1. `BetterCurseForge.injectCSS(css)`
|
||||||
Injects custom CSS dynamically into `<head>`. Returns a cleanup function that unmounts the style tag:
|
Injects CSS into `<head>`. Returns a function that removes the style tag:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const removeStyles = BetterCurseForge.injectCSS(`
|
const removeStyles = BetterCurseForge.injectCSS(`
|
||||||
|
|
@ -98,7 +98,7 @@ plugin.onDisable = () => {
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. `BetterCurseForge.settings`
|
### 2. `BetterCurseForge.settings`
|
||||||
Stores and retrieves persistent settings saved to disk (`settings.json`):
|
Reads and writes settings that are saved to disk (`settings.json`):
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Retrieve a stored setting (or null if not set)
|
// Retrieve a stored setting (or null if not set)
|
||||||
|
|
@ -111,10 +111,10 @@ await BetterCurseForge.settings.set('compactMode.density', 'ultra');
|
||||||
---
|
---
|
||||||
|
|
||||||
### 3. `BetterCurseForge.patcher`
|
### 3. `BetterCurseForge.patcher`
|
||||||
Safe method monkey-patching for objects and functions:
|
Helpers for monkey-patching methods on objects. Each call returns a function that undoes the patch:
|
||||||
|
|
||||||
#### `patcher.before(object, methodName, callback)`
|
#### `patcher.before(object, methodName, callback)`
|
||||||
Runs your callback before the target method executes. Receives `(args)`:
|
Runs your callback before the original method. The callback gets `(args)`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const unpatch = BetterCurseForge.patcher.before(console, 'error', (args) => {
|
const unpatch = BetterCurseForge.patcher.before(console, 'error', (args) => {
|
||||||
|
|
@ -125,7 +125,7 @@ plugin.onDisable = () => unpatch();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `patcher.after(object, methodName, callback)`
|
#### `patcher.after(object, methodName, callback)`
|
||||||
Runs after the target method completes. Receives `(args, result)`. You can return a modified value:
|
Runs after the original method. The callback gets `(args, result)` and can return a new value to replace the result:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const unpatch = BetterCurseForge.patcher.after(targetObj, 'getItemTitle', (args, result) => {
|
const unpatch = BetterCurseForge.patcher.after(targetObj, 'getItemTitle', (args, result) => {
|
||||||
|
|
@ -136,7 +136,7 @@ plugin.onDisable = () => unpatch();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `patcher.instead(object, methodName, callback)`
|
#### `patcher.instead(object, methodName, callback)`
|
||||||
Completely supersedes the target method. Receives `(args, originalMethod)`:
|
Replaces the original method. The callback gets `(args, originalMethod)`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const unpatch = BetterCurseForge.patcher.instead(targetObj, 'fetchData', (args, originalFn) => {
|
const unpatch = BetterCurseForge.patcher.instead(targetObj, 'fetchData', (args, originalFn) => {
|
||||||
|
|
@ -150,7 +150,7 @@ plugin.onDisable = () => unpatch();
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4. `BetterCurseForge.on(event, callback)` / `.off()` / `.emit()`
|
### 4. `BetterCurseForge.on(event, callback)` / `.off()` / `.emit()`
|
||||||
Application-wide event emitter:
|
An event emitter shared across the app:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const onReady = () => console.log('CurseForge UI is fully loaded!');
|
const onReady = () => console.log('CurseForge UI is fully loaded!');
|
||||||
|
|
@ -164,9 +164,9 @@ plugin.onDisable = () => {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Complete Example: Compact Mode Plugin
|
## Example: Compact Mode
|
||||||
|
|
||||||
This plugin adds a high-density layout mode to CurseForge.
|
This plugin makes CurseForge's layout denser.
|
||||||
|
|
||||||
::: code-group
|
::: code-group
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,33 @@
|
||||||
# Custom Themes & Theming Guide
|
# Themes
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## In-App Theme Selector
|
## Picking a theme
|
||||||
|
|
||||||
BetterCurseForge embeds client themes directly into CurseForge's native **Settings -> Themes** menu.
|
Client themes are added to CurseForge's own **Settings -> Themes** page.
|
||||||
|
|
||||||
### How to Access Client Themes
|
### Finding client themes
|
||||||
1. Open CurseForge Launcher.
|
1. Open CurseForge.
|
||||||
2. Click the **Settings** gear icon in the bottom-left corner.
|
2. Click the **Settings** gear icon in the bottom-left corner.
|
||||||
3. Select **Themes** from the settings sidebar.
|
3. Choose **Themes** in the settings sidebar.
|
||||||
4. Scroll down past the *Classic*, *Colorblind*, and *Premium* categories to find the **Client Themes** section:
|
4. Scroll past the *Classic*, *Colorblind* and *Premium* categories to reach **Client Themes**:
|
||||||
|
|
||||||
{width=48 style="display:block;margin:1rem auto;"}
|
{width=48 style="display:block;margin:1rem auto;"}
|
||||||
|
|
||||||
### Native Features in Settings -> Themes
|
### What the theme page does
|
||||||
- **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.
|
- **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.
|
||||||
- **Mutual Exclusivity**: Selecting a client theme seamlessly deselects native skins, and vice versa. Only one theme is active at a time.
|
- **One theme at a time**: Picking a client theme deselects any native skin, and the other way around.
|
||||||
- **"Open Folder" Button**: Directly opens your themes folder in Windows Explorer so you can drop in new stylesheets.
|
- **Open Folder button**: Opens your themes folder in Explorer so you can drop in new stylesheets.
|
||||||
- **"Refresh" Button**: Reloads all installed custom themes from disk instantly without needing to restart CurseForge.
|
- **Refresh button**: Reloads your custom themes from disk, no restart needed.
|
||||||
- **Native Premium Themes Unlocked**: All official CurseForge Premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge*, and *Spookyforge*) are permanently unlocked and free to use!
|
- **Premium themes unlocked**: The official Premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge* and *Spookyforge*) are unlocked and free to use.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Theme Folder Location
|
## Theme folder
|
||||||
|
|
||||||
Themes are stored in your user application data directory:
|
Themes live in your user application data directory:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
%APPDATA%\CurseForge\BetterCurseForge\themes\
|
%APPDATA%\CurseForge\BetterCurseForge\themes\
|
||||||
|
|
@ -38,16 +38,16 @@ Full path:
|
||||||
C:\Users\<YourUsername>\AppData\Roaming\CurseForge\BetterCurseForge\themes\
|
C:\Users\<YourUsername>\AppData\Roaming\CurseForge\BetterCurseForge\themes\
|
||||||
```
|
```
|
||||||
|
|
||||||
You can open this folder anytime by clicking the **Open Folder** button in **Settings -> Themes**.
|
The **Open Folder** button in **Settings -> Themes** opens it for you.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Theme File Structure
|
## Theme layout
|
||||||
|
|
||||||
BetterCurseForge supports two convenient formats:
|
There are two ways to lay out a theme:
|
||||||
|
|
||||||
### 1. Single-File Theme (Recommended)
|
### 1. Single file (recommended)
|
||||||
Place a `.css` file in the `themes/` directory, with an optional accompanying `.json` manifest:
|
Put a `.css` file in `themes/`, with an optional `.json` manifest next to it:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
themes/
|
themes/
|
||||||
|
|
@ -55,8 +55,8 @@ themes/
|
||||||
└── purple-night.json (optional metadata & preview colors)
|
└── purple-night.json (optional metadata & preview colors)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Folder-Based Theme
|
### 2. Folder
|
||||||
Create a folder inside `themes/` with a `theme.css` and `theme.json`:
|
Make a folder inside `themes/` containing `theme.css` and `theme.json`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
themes/
|
themes/
|
||||||
|
|
@ -68,9 +68,9 @@ themes/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Theme Manifest Format (`theme.json`)
|
## Manifest (`theme.json`)
|
||||||
|
|
||||||
A `.json` manifest provides metadata and defines the miniature preview card colors shown in the **Settings -> Themes** menu:
|
The manifest holds the theme's metadata and the colors of the small preview card shown in **Settings -> Themes**:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -85,78 +85,78 @@ A `.json` manifest provides metadata and defines the miniature preview card colo
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manifest Fields
|
### Fields
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| `name` | `string` | Display name shown on the theme card and in settings. |
|
| `name` | `string` | Name shown on the theme card and in settings. |
|
||||||
| `version` | `string` | Semantic version string (e.g. `1.0.0`). |
|
| `version` | `string` | Semantic version (e.g. `1.0.0`). |
|
||||||
| `description` | `string` | Short tooltip description displayed when hovering the title. |
|
| `description` | `string` | Short description, shown as a tooltip when you hover the title. |
|
||||||
| `author` | `string` | Author name or handle. |
|
| `author` | `string` | Author name or handle. |
|
||||||
| `primaryColor` | `string` | Hex color for the primary square icon in the thumbnail card. |
|
| `primaryColor` | `string` | Hex color of the square icon on the preview card. |
|
||||||
| `titleColor` | `string` | Hex color for the top bar in the thumbnail card. |
|
| `titleColor` | `string` | Hex color of the top bar on the preview card. |
|
||||||
| `bodyColor` | `string` | Hex color for the lower text bar in the thumbnail card. |
|
| `bodyColor` | `string` | Hex color of the lower text bar on the preview card. |
|
||||||
| `backgroundColor`| `string` | Hex background color for the thumbnail card itself. |
|
| `backgroundColor`| `string` | Hex background color of the preview card. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CurseForge CSS Variables Reference
|
## CSS variables
|
||||||
|
|
||||||
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.
|
CurseForge is styled with CSS custom properties. Overriding them lets you theme most of the app without touching individual class names.
|
||||||
|
|
||||||
### Brand & Accents
|
### Brand & accents
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `--surface-brand-brand` | Primary brand accent (active tabs, primary buttons, indicators). |
|
| `--surface-brand-brand` | Main brand accent: active tabs, primary buttons, indicators. |
|
||||||
| `--surface-brand-brand-hover` | Hover state for brand-colored elements. |
|
| `--surface-brand-brand-hover` | Hover state for brand-colored elements. |
|
||||||
| `--color-primary` | Main accent color. |
|
| `--color-primary` | Main accent color. |
|
||||||
| `--color-accent` | Secondary highlight color. |
|
| `--color-accent` | Secondary highlight color. |
|
||||||
| `--selected-item-border` | Border style for selected items and active inputs. |
|
| `--selected-item-border` | Border style for selected items and active inputs. |
|
||||||
|
|
||||||
### Surfaces & Backgrounds
|
### Surfaces & backgrounds
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `--color-background` | Main application background (content views, modpack library). |
|
| `--color-background` | Main app background (content views, modpack library). |
|
||||||
| `--surface-base` | Darkest background layer (scroll tracks, container bases). |
|
| `--surface-base` | Darkest background layer (scroll tracks, container bases). |
|
||||||
| `--surface-primary` | Elevated primary surface (sidebars, sub-panels). |
|
| `--surface-primary` | Raised surface (sidebars, sub-panels). |
|
||||||
| `--surface-secondary` | Secondary surface layer (navigation menus, dialog panels). |
|
| `--surface-secondary` | Second surface layer (navigation menus, dialog panels). |
|
||||||
| `--surface-tertiery` | Tertiary surface layer (mod cards, list row containers). |
|
| `--surface-tertiery` | Third surface layer (mod cards, list row containers). |
|
||||||
| `--surface-on-surface` | Subtle translucent overlay for buttons (`rgba(...)`). |
|
| `--surface-on-surface` | Faint translucent overlay for buttons (`rgba(...)`). |
|
||||||
| `--surface-on-surface-hover` | Translucent hover overlay for buttons. |
|
| `--surface-on-surface-hover` | Translucent hover overlay for buttons. |
|
||||||
|
|
||||||
### Modpack & Library Cards
|
### Modpack & library cards
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `--surface-card` | Modpack / project card base background. |
|
| `--surface-card` | Base background of modpack and project cards. |
|
||||||
| `--surface-card-hover` | Background color when hovering over modpack cards. |
|
| `--surface-card-hover` | Background when hovering over a modpack card. |
|
||||||
| `--color-surface-card-active` | Active or clicked state for card items. |
|
| `--color-surface-card-active` | Active or clicked state of a card. |
|
||||||
|
|
||||||
### Typography & Text
|
### Typography & text
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `--text-pure` | Pure white text (`#ffffff`). |
|
| `--text-pure` | Pure white text (`#ffffff`). |
|
||||||
| `--text-primary` | Primary readable text color. |
|
| `--text-primary` | Main readable text color. |
|
||||||
| `--color-text-title` | Headings, modpack titles, section names. |
|
| `--color-text-title` | Headings, modpack titles, section names. |
|
||||||
| `--color-text-body` | Standard body and description text. |
|
| `--color-text-body` | Normal body and description text. |
|
||||||
| `--color-text-secondary` | Subtitles, authors, version numbers. |
|
| `--color-text-secondary` | Subtitles, authors, version numbers. |
|
||||||
|
|
||||||
### Title Bar & Navigation
|
### Title bar & navigation
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `--title-bar-background` | Background color or gradient for the top window title bar. |
|
| `--title-bar-background` | Background color or gradient of the window title bar. |
|
||||||
| `--btn-window-action` | Minimize, maximize, and close button icons. |
|
| `--btn-window-action` | Icons on the minimize, maximize and close buttons. |
|
||||||
| `--color-scrollbar-thumb` | Scrollbar thumb handle color. |
|
| `--color-scrollbar-thumb` | Scrollbar thumb color. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Example Theme: Emerald Night
|
## Example: Emerald Night
|
||||||
|
|
||||||
Here is a complete, working theme stylesheet you can save as `emerald-night.css`:
|
A full working theme. Save the stylesheet as `emerald-night.css` and the manifest as `emerald-night.json`:
|
||||||
|
|
||||||
::: code-group
|
::: code-group
|
||||||
|
|
||||||
|
|
@ -237,12 +237,12 @@ button[class*="primary"]:not([class*="secondary"]):hover {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Tips for Inspecting Elements
|
## Inspecting elements
|
||||||
|
|
||||||
Because CurseForge runs on Electron and React, you can inspect any UI element using Chromium DevTools:
|
CurseForge is an Electron and React app, so you can inspect its UI with Chromium DevTools:
|
||||||
|
|
||||||
1. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd> in CurseForge to open 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 activate the element inspector.
|
2. Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd> to turn on the element inspector.
|
||||||
3. Click on any button, banner, header, or list item to reveal its computed styles and classes in the **Elements** panel.
|
3. Click a button, banner, header or list item to see 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.
|
4. Try CSS changes in the **Styles** tab before adding them to your theme.
|
||||||
5. In **Settings -> Themes**, click **Refresh** to instantly apply your modified theme stylesheet!
|
5. In **Settings -> Themes**, click **Refresh** to load your updated stylesheet.
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ layout: home
|
||||||
|
|
||||||
hero:
|
hero:
|
||||||
name: "BetterCurseForge"
|
name: "BetterCurseForge"
|
||||||
text: "Client mod for CurseForge Launcher"
|
text: "A client mod for the CurseForge launcher"
|
||||||
tagline: "Ad-free experience, custom themes, unlocked premium skins, and plugins."
|
tagline: "Ad blocking, custom themes, unlocked premium skins, and plugins."
|
||||||
image:
|
image:
|
||||||
src: /logo.png
|
src: /logo.png
|
||||||
alt: BetterCurseForge
|
alt: BetterCurseForge
|
||||||
|
|
@ -18,30 +18,30 @@ hero:
|
||||||
|
|
||||||
features:
|
features:
|
||||||
- icon: 🚫
|
- icon: 🚫
|
||||||
title: Complete Ad Removal
|
title: Ad blocking
|
||||||
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.
|
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.
|
||||||
- icon: 🎨
|
- icon: 🎨
|
||||||
title: Client Themes & Free Skins
|
title: Themes and free skins
|
||||||
details: Integrated directly into Settings -> Themes with live hover previews, theme folder shortcuts, and all native CurseForge Premium themes unlocked for free.
|
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.
|
||||||
- icon: 🔄
|
- icon: 🔄
|
||||||
title: Auto-Repatch & Smooth Updates
|
title: Auto-repatch after updates
|
||||||
details: Survives launcher updates effortlessly. The active interceptor suppresses unpatched windows, and a sleek splash GUI displays live progress while repatching in seconds.
|
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.
|
||||||
- icon: 🖥️
|
- icon: 🖥️
|
||||||
title: Modern GUI & CLI
|
title: GUI and CLI
|
||||||
details: Includes an intuitive dark-themed desktop application with one-click actions, as well as a full command-line interface for power users.
|
details: A dark-themed desktop app with one-click actions, plus a command-line interface for scripting.
|
||||||
- icon: 🧩
|
- icon: 🧩
|
||||||
title: Plugin System
|
title: Plugins
|
||||||
details: Extend launcher capabilities with lightweight JavaScript plugins using our runtime monkey-patcher and persistent settings API.
|
details: Write small JavaScript plugins that patch methods at runtime and keep their settings between restarts.
|
||||||
- icon: 🛡️
|
- icon: 🛡️
|
||||||
title: Safe & 100% Reversible
|
title: Backups and unpatching
|
||||||
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.
|
details: Original files are backed up first (`app.asar.vanilla` and `CurseForge.exe.vanilla`), and you can restore stock CurseForge with one click.
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="quickstart-section" style="margin-top: 3rem; text-align: center;">
|
<div class="quickstart-section" style="margin-top: 3rem; text-align: center;">
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
Download `BetterCurseForge.exe` and patch in one click:
|
Download `BetterCurseForge.exe`, then open it for the GUI or patch from a terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Launch the desktop GUI:
|
# Launch the desktop GUI:
|
||||||
|
|
@ -57,14 +57,15 @@ BetterCurseForge.exe patch
|
||||||
|
|
||||||
## Why BetterCurseForge?
|
## Why BetterCurseForge?
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
**BetterCurseForge** restores a clean, fast, and customizable experience:
|
BetterCurseForge changes that:
|
||||||
|
|
||||||
- **Seamless Native UI**: Client themes appear natively under **Settings -> Themes** alongside official themes, complete with real-time live hover previews.
|
- **Native theme picker**: Client themes appear in **Settings -> Themes** next to the official ones, with live hover previews.
|
||||||
- **Unlocked Premium Themes**: Access all native CurseForge Premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Spookyforge*) without a subscription.
|
- **Premium themes unlocked**: The official premium themes (*Pixelforge*, *Lifeforge*, *Fantasyforge*, *Spookyforge*) work without a subscription.
|
||||||
- **Zero-Ad Layouts**: Ad columns and sidebars are eliminated from the layout, letting modpack grids expand to fill your entire display.
|
- **No ad columns**: Ad columns and sidebars are collapsed, so modpack grids use the full width of the window.
|
||||||
- **Automated Update Resistance**: Never worry about CurseForge updates breaking your setup—**Auto-Repatch** automatically detects updates and re-applies the patch in seconds.
|
- **Survives updates**: When CurseForge updates, BetterCurseForge detects it and reapplies the patch in a few seconds.
|
||||||
- **One-Click Reversible**: Easily unpatch anytime via the GUI or `BetterCurseForge.exe unpatch` to restore vanilla CurseForge.
|
- **Easy to undo**: Unpatch from the GUI or with `BetterCurseForge.exe unpatch` to get vanilla CurseForge back.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue