Compare commits

..

No commits in common. "main" and "release" have entirely different histories.

25 changed files with 1283 additions and 5584 deletions

View file

@ -1,107 +0,0 @@
name: Release
on:
push:
tags:
- "v*.*.*"
permissions:
contents: write
jobs:
build-linux:
name: Build Linux AppImage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Install Linux build deps
run: |
sudo apt-get update
sudo apt-get install -y libarchive-tools rpm fakeroot
- name: Build AppImage
run: npm run dist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: linux-dist
path: dist/*.AppImage
if-no-files-found: error
build-windows:
name: Build Windows Installer
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build Windows installer
run: npm run dist:win
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: windows-dist
path: |
dist/*.exe
dist/*.msi
if-no-files-found: error
build-mac:
name: Build macOS DMG
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Clear electron-builder cache
run: rm -rf ~/Library/Caches/electron-builder
- name: Build macOS DMG
run: npm run dist:mac
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: mac-dist
path: dist/*.dmg
if-no-files-found: error
release:
name: Create GitHub Release
needs: [build-linux, build-windows, build-mac]
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create release and upload assets
uses: softprops/action-gh-release@v2
with:
name: LegacyLauncher ${{ github.ref_name }}
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
generate_release_notes: true
files: |
artifacts/linux-dist/*
artifacts/windows-dist/*
artifacts/mac-dist/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,129 +0,0 @@
name: Release
on:
push:
tags:
- "v*.*.*"
permissions:
contents: write
jobs:
build-linux:
name: Build Linux AppImage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Install Linux build deps
run: |
sudo apt-get update
sudo apt-get install -y libarchive-tools rpm fakeroot
- name: Build AppImage
run: npm run dist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: linux-dist
path: dist/*.AppImage
if-no-files-found: error
build-windows:
name: Build Windows Installer
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build Windows installer
run: npm run dist:win
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: windows-dist
path: |
dist/*.exe
dist/*.msi
if-no-files-found: error
build-mac:
name: Build macOS DMG (Apple Silicon)
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build macOS DMG
run: npm run dist:mac
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: mac-dist
path: dist/*.dmg
if-no-files-found: error
build-mac-intel:
name: Build macOS DMG (Intel)
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build macOS DMG (Intel)
run: npm run dist:mac -- --x64
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload macOS Intel artifact
uses: actions/upload-artifact@v4
with:
name: mac-intel-dist
path: dist/*.dmg
if-no-files-found: error
release:
name: Create GitHub Release
needs: [build-linux, build-windows, build-mac, build-mac-intel]
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create release and upload assets
uses: softprops/action-gh-release@v2
with:
name: LegacyLauncher ${{ github.ref_name }}
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
generate_release_notes: true
files: |
artifacts/linux-dist/*
artifacts/windows-dist/*
artifacts/mac-dist/*
artifacts/mac-intel-dist/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View file

@ -1 +0,0 @@
node_modules/

BIN
256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

21
LICENSE
View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2026 gardenGnostic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

BIN
Minecraft.ttf Normal file

Binary file not shown.

View file

@ -2,11 +2,6 @@
A custom launcher for Minecraft Legacy Console Edition. A custom launcher for Minecraft Legacy Console Edition.
<img width="1277" height="717" alt="image" src="https://github.com/user-attachments/assets/eaa9bae6-3b3b-4e39-a3c1-156e34abf3cc" />
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/gradengnostic)
## Features ## Features
- **Minecraft-style GUI**: Authentic pixelated interface with Minecraft font and styling - **Minecraft-style GUI**: Authentic pixelated interface with Minecraft font and styling
@ -79,11 +74,6 @@ The launcher supports several compatibility options for Linux:
- **extract-zip**: ZIP archive extraction - **extract-zip**: ZIP archive extraction
- **Tailwind CSS**: UI styling (via CDN) - **Tailwind CSS**: UI styling (via CDN)
## Assets
- Controller button sprites: [greatdocbrown](https://greatdocbrown.itch.io/gamepad-ui)
- UI Sounds: Using the free version of [JDSherbert's Ultimate UI SFX Pack on itch.io](https://jdsherbert.itch.io/ultimate-ui-sfx-pack)
## Development ## Development
The launcher is built with: The launcher is built with:
@ -109,13 +99,3 @@ The launcher is built with:
## Contributing ## Contributing
Feel free to submit issues and pull requests for improvements.</content> Feel free to submit issues and pull requests for improvements.</content>
## Star History
<a href="https://www.star-history.com/?repos=gradenGnostic%2FLegacyLauncher&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=gradenGnostic/LegacyLauncher&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=gradenGnostic/LegacyLauncher&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=gradenGnostic/LegacyLauncher&type=date&legend=top-left" />
</picture>
</a>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

1444
index.html

File diff suppressed because it is too large Load diff

204
main.js
View file

@ -1,159 +1,103 @@
const { app, BrowserWindow, shell, ipcMain, dialog, globalShortcut, desktopCapturer } = require('electron'); const { app, BrowserWindow, shell, ipcMain } = require('electron');
const path = require('path'); const path = require('path');
const Store = require('electron-store'); const DiscordRPC = require('discord-rpc');
const fs = require('fs');
const https = require('https');
const extractZip = require('extract-zip');
const { exec } = require('child_process');
const store = new Store(); const clientId = '1346541144141103114';
let mainWindow = null; let rpc;
let isGameRunning = false;
function initRPC() {
rpc = new DiscordRPC.Client({ transport: 'ipc' });
rpc.on('ready', () => {
console.log('Discord RPC ready');
setActivity();
});
rpc.on('error', (err) => {
console.error('Discord RPC Error:', err);
});
rpc.on('disconnected', () => {
console.log('Discord RPC disconnected, retrying...');
setTimeout(connectRPC, 15000);
});
connectRPC();
}
function connectRPC() {
rpc.login({ clientId }).catch(err => {
console.log('Discord RPC connection failed, retrying in 20s...');
setTimeout(connectRPC, 20000);
});
}
initRPC();
function setActivity(details = 'In Menus', state = 'Ready to Play', startTime = null) {
if (!rpc || !rpc.user) return;
const activity = {
details: details,
state: state,
largeImageKey: 'logo',
largeImageText: 'LegacyLauncher',
instance: false,
};
if (startTime) {
activity.startTimestamp = startTime;
}
rpc.setActivity(activity).catch(() => {
});
}
function createWindow() { function createWindow() {
mainWindow = new BrowserWindow({ const win = new BrowserWindow({
width: 1280, width: 1280,
height: 720, height: 720,
minWidth: 1024, minWidth: 1024,
minHeight: 600, minHeight: 600,
center: true,
resizable: true, resizable: true,
frame: false, frame: false,
icon: path.join(__dirname, '512x512.png'), icon: path.join(__dirname, '256x256.png'),
transparent: true, transparent: true,
autoHideMenuBar: true, autoHideMenuBar: true,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false, contextIsolation: false,
enableRemoteModule: true enableRemoteModule: true
} }
}); });
mainWindow.loadFile('index.html'); win.maximize();
win.loadFile('index.html');
ipcMain.on('window-minimize', () => mainWindow.minimize()); // Handle window controls
ipcMain.on('window-minimize', () => win.minimize());
ipcMain.on('window-maximize', () => { ipcMain.on('window-maximize', () => {
if (mainWindow.isMaximized()) { if (win.isMaximized()) {
mainWindow.unmaximize(); win.unmaximize();
} else { } else {
mainWindow.maximize(); win.maximize();
} }
}); });
ipcMain.on('window-close', () => mainWindow.close()); ipcMain.on('window-close', () => win.close());
ipcMain.on('window-fullscreen', () => {
mainWindow.setFullScreen(!mainWindow.isFullScreen());
});
ipcMain.on('window-set-fullscreen', (event, enabled) => {
mainWindow.setFullScreen(Boolean(enabled));
});
ipcMain.handle('take-screenshot', async (event) => {
try {
const screenshotsDir = path.join(app.getPath('userData'), 'Screenshots');
if (!fs.existsSync(screenshotsDir)) {
fs.mkdirSync(screenshotsDir, { recursive: true });
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = `screenshot-${timestamp}.png`;
const filePath = path.join(screenshotsDir, fileName);
if (isGameRunning) {
const sources = await desktopCapturer.getSources({
types: ['window', 'screen'],
thumbnailSize: { width: 3840, height: 2160 } // High res for screenshot
});
// Find Minecraft window or fallback to primary screen
const source = sources.find(s => s.name.toLowerCase().includes('minecraft')) ||
sources.find(s => s.id.startsWith('screen'));
if (source) {
fs.writeFileSync(filePath, source.thumbnail.toPNG());
return filePath;
}
}
// Fallback to launcher capture if game isn't running or not found
const win = BrowserWindow.fromWebContents(event.sender) || mainWindow;
if (!win) throw new Error("Window not found");
const image = await win.capturePage();
fs.writeFileSync(filePath, image.toPNG());
return filePath;
} catch (err) {
console.error("Screenshot capture error:", err);
throw err;
}
});
ipcMain.on('game-running-state', (event, running) => {
isGameRunning = running;
if (running) {
globalShortcut.register('F2', () => {
if (mainWindow) {
mainWindow.webContents.send('trigger-screenshot');
}
});
} else {
globalShortcut.unregister('F2');
}
});
ipcMain.handle('list-screenshots', async () => {
const screenshotsDir = path.join(app.getPath('userData'), 'Screenshots');
if (!fs.existsSync(screenshotsDir)) {
return [];
}
const files = fs.readdirSync(screenshotsDir);
return files
.filter(f => f.toLowerCase().endsWith('.png'))
.sort((a, b) => {
try {
// Extract timestamp from 'screenshot-YYYY-MM-DDTHH-mm-ss-SSSZ.png'
const timeA = a.replace('screenshot-', '').replace('.png', '').replace(/-/g, ':');
const timeB = b.replace('screenshot-', '').replace('.png', '').replace(/-/g, ':');
return new Date(timeB) - new Date(timeA);
} catch (e) {
return 0;
}
})
.map(f => ({
name: f,
path: path.join(screenshotsDir, f),
url: `file://${path.join(screenshotsDir, f)}`
}));
});
ipcMain.handle('delete-screenshot', async (event, fileName) => {
const filePath = path.join(app.getPath('userData'), 'Screenshots', fileName);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
return true;
}
return false;
});
ipcMain.handle('open-screenshots-dir', async () => {
const screenshotsDir = path.join(app.getPath('userData'), 'Screenshots');
if (!fs.existsSync(screenshotsDir)) {
fs.mkdirSync(screenshotsDir, { recursive: true });
}
shell.openPath(screenshotsDir);
});
ipcMain.handle('store-get', (event, key) => store.get(key));
ipcMain.handle('store-set', (event, key, value) => store.set(key, value));
ipcMain.handle('select-directory', async () => {
const result = await dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory']
});
return result.filePaths[0];
});
mainWindow.on('maximize', () => mainWindow.webContents.send('window-is-maximized', true)); ipcMain.on('update-rpc', (event, data) => {
mainWindow.on('unmaximize', () => mainWindow.webContents.send('window-is-maximized', false)); setActivity(data.details, data.state, data.startTime);
});
mainWindow.webContents.setWindowOpenHandler(({ url }) => { win.on('maximize', () => win.webContents.send('window-is-maximized', true));
win.on('unmaximize', () => win.webContents.send('window-is-maximized', false));
win.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url); shell.openExternal(url);
return { action: 'deny' }; return { action: 'deny' };
}); });

BIN
minecraft.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

View file

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 320 KiB

350
package-lock.json generated
View file

@ -1,15 +1,15 @@
{ {
"name": "legacylauncher", "name": "legacylauncher",
"version": "3.5.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "legacylauncher", "name": "legacylauncher",
"version": "3.5.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"electron-store": "^6.0.1", "discord-rpc": "^4.0.1",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
@ -913,6 +913,7 @@
"version": "6.14.0", "version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@ -1197,15 +1198,6 @@
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
}, },
"node_modules/atomically": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz",
"integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==",
"license": "MIT",
"engines": {
"node": ">=10.12.0"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@ -1237,6 +1229,16 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": { "node_modules/bl": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -1710,42 +1712,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/conf": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/conf/-/conf-7.1.2.tgz",
"integrity": "sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==",
"license": "MIT",
"dependencies": {
"ajv": "^6.12.2",
"atomically": "^1.3.1",
"debounce-fn": "^4.0.0",
"dot-prop": "^5.2.0",
"env-paths": "^2.2.0",
"json-schema-typed": "^7.0.3",
"make-dir": "^3.1.0",
"onetime": "^5.1.0",
"pkg-up": "^3.1.0",
"semver": "^7.3.2"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/conf/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@ -1812,30 +1778,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/debounce-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz",
"integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==",
"license": "MIT",
"dependencies": {
"mimic-fn": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/debounce-fn/node_modules/mimic-fn": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
"integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -2013,6 +1955,19 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/discord-rpc": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-4.0.1.tgz",
"integrity": "sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.1",
"ws": "^7.3.1"
},
"optionalDependencies": {
"register-scheme": "github:devsnek/node-register-scheme"
}
},
"node_modules/dmg-builder": { "node_modules/dmg-builder": {
"version": "26.8.1", "version": "26.8.1",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz",
@ -2095,18 +2050,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/dot-prop": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
"integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
"license": "MIT",
"dependencies": {
"is-obj": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.6.1", "version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
@ -2325,31 +2268,6 @@
"node": ">= 10.0.0" "node": ">= 10.0.0"
} }
}, },
"node_modules/electron-store": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/electron-store/-/electron-store-6.0.1.tgz",
"integrity": "sha512-8rdM0XEmDGsLuZM2oRABzsLX+XmD5x3rwxPMEPv0MrN9/BWanyy3ilb2v+tCrKtIZVF3MxUiZ9Bfqe8e0popKQ==",
"license": "MIT",
"dependencies": {
"conf": "^7.1.2",
"type-fest": "^0.16.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/electron-store/node_modules/type-fest": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/electron-winstaller": { "node_modules/electron-winstaller": {
"version": "5.4.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
@ -2399,7 +2317,6 @@
"version": "0.1.13", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
@ -2419,6 +2336,7 @@
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -2554,12 +2472,14 @@
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-json-stable-stringify": { "node_modules/fast-json-stable-stringify": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fd-slicer": { "node_modules/fd-slicer": {
@ -2589,6 +2509,13 @@
} }
} }
}, },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT",
"optional": true
},
"node_modules/filelist": { "node_modules/filelist": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
@ -2629,18 +2556,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"license": "MIT",
"dependencies": {
"locate-path": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@ -3097,7 +3012,7 @@
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
@ -3186,15 +3101,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-unicode-supported": { "node_modules/is-unicode-supported": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@ -3299,14 +3205,9 @@
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/json-schema-typed": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz",
"integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==",
"license": "BSD-2-Clause"
},
"node_modules/json-stringify-safe": { "node_modules/json-stringify-safe": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@ -3355,19 +3256,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"license": "MIT",
"dependencies": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.23", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
@ -3415,21 +3303,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"license": "MIT",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-fetch-happen": { "node_modules/make-fetch-happen": {
"version": "14.0.3", "version": "14.0.3",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz",
@ -3517,6 +3390,7 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -3750,7 +3624,6 @@
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
@ -3777,6 +3650,26 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-gyp": { "node_modules/node-gyp": {
"version": "11.5.0", "version": "11.5.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz",
@ -3868,6 +3761,7 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"mimic-fn": "^2.1.0" "mimic-fn": "^2.1.0"
@ -3929,33 +3823,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"license": "MIT",
"dependencies": {
"p-limit": "^2.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/p-locate/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-map": { "node_modules/p-map": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
@ -3969,15 +3836,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/package-json-from-dist": { "node_modules/package-json-from-dist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@ -3985,15 +3843,6 @@
"dev": true, "dev": true,
"license": "BlueOak-1.0.0" "license": "BlueOak-1.0.0"
}, },
"node_modules/path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -4079,18 +3928,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pkg-up": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
"license": "MIT",
"dependencies": {
"find-up": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/plist": { "node_modules/plist": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
@ -4196,6 +4033,7 @@
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -4242,6 +4080,17 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/register-scheme": {
"version": "0.0.2",
"resolved": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"bindings": "^1.3.0",
"node-addon-api": "^1.3.0"
}
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -4373,7 +4222,7 @@
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/sanitize-filename": { "node_modules/sanitize-filename": {
@ -4400,6 +4249,7 @@
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -4690,9 +4540,9 @@
} }
}, },
"node_modules/tar": { "node_modules/tar": {
"version": "7.5.11", "version": "7.5.10",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
"integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", "integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
"dev": true, "dev": true,
"license": "BlueOak-1.0.0", "license": "BlueOak-1.0.0",
"dependencies": { "dependencies": {
@ -4837,6 +4687,12 @@
"tmp": "^0.2.0" "tmp": "^0.2.0"
} }
}, },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/truncate-utf8-bytes": { "node_modules/truncate-utf8-bytes": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
@ -4908,6 +4764,7 @@
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"punycode": "^2.1.0" "punycode": "^2.1.0"
@ -4953,6 +4810,22 @@
"defaults": "^1.0.3" "defaults": "^1.0.3"
} }
}, },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/which": { "node_modules/which": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
@ -5012,6 +4885,27 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/ws": {
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"license": "MIT",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlbuilder": { "node_modules/xmlbuilder": {
"version": "15.1.1", "version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",

View file

@ -1,15 +1,13 @@
{ {
"name": "legacylauncher", "name": "legacylauncher",
"version": "3.5.0", "version": "1.0.0",
"description": "A Minecraft: Legacy Console Edition launcher", "description": "",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "electron .", "start": "electron .",
"dist": "electron-builder --linux AppImage", "dist": "electron-builder --linux AppImage",
"dist:flatpak": "electron-builder --linux flatpak", "dist:win": "electron-builder --win nsis"
"dist:win": "electron-builder --win nsis",
"dist:mac": "electron-builder --mac dmg"
}, },
"build": { "build": {
"appId": "com.legacylauncher.app", "appId": "com.legacylauncher.app",
@ -18,7 +16,7 @@
"target": [ "target": [
"nsis" "nsis"
], ],
"icon": "512x512.png" "icon": "256x256.png"
}, },
"nsis": { "nsis": {
"oneClick": false, "oneClick": false,
@ -26,28 +24,10 @@
}, },
"linux": { "linux": {
"target": [ "target": [
"AppImage", "AppImage"
"flatpak"
], ],
"category": "Game", "category": "Game",
"icon": "512x512.png", "icon": "256x256.png"
"desktop": {
"entry": {
"Name": "Minecraft LCE Launcher",
"GenericName": "Minecraft Launcher",
"Comment": "A Minecraft: Legacy Console Edition launcher",
"Categories": "Game;Emulation;",
"StartupNotify": "true"
}
},
"executableName": "legacylauncher"
},
"mac": {
"target": [
"dmg"
],
"icon": "512x512.png",
"category": "public.app-category.games"
}, },
"files": [ "files": [
"**/*", "**/*",
@ -58,7 +38,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"electron-store": "^6.0.1", "discord-rpc": "^4.0.1",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {

File diff suppressed because it is too large Load diff

View file

@ -1,469 +0,0 @@
// skin_manager.js
// Handles skin uploading, conversion, 3D preview, and saving to char.png
let mainMenuScene, mainMenuCamera, mainMenuRenderer, mainMenuPlayerGroup;
let isMainSkinDragging = false;
let mainMenuSkinRenderMode = '3d';
let skinScene, skinCamera, skinRenderer, skinPlayerGroup;
let isSkinDragging = false;
let previousSkinMousePosition = { x: 0, y: 0 };
let processedSkinDataUrl = null;
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
const skinInput = document.getElementById('skin-input');
const dropZone = document.getElementById('drop-zone');
const saveSkinBtn = document.getElementById('save-skin-btn');
const closeSkinBtn = document.getElementById('btn-close-skin');
if (skinInput) skinInput.addEventListener('change', (e) => handleSkinFile(e.target.files[0]));
if (dropZone) {
dropZone.addEventListener('click', () => skinInput?.click());
dropZone.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
skinInput?.click();
}
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-green-500');
});
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('border-green-500'));
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-green-500');
handleSkinFile(e.dataTransfer.files[0]);
});
}
if (saveSkinBtn) {
saveSkinBtn.addEventListener('click', saveSkinToDisk);
}
if (closeSkinBtn) {
closeSkinBtn.addEventListener('click', closeSkinManager);
}
// Initialize Main Menu Viewer
initMainMenuSkinViewer();
});
function initMainMenuSkinViewer() {
const container = document.getElementById('main-skin-viewer');
if (!container) return;
mainMenuScene = new THREE.Scene();
mainMenuCamera = new THREE.PerspectiveCamera(45, container.offsetWidth / container.offsetHeight, 0.1, 1000);
mainMenuCamera.position.set(0, 5, 60);
mainMenuRenderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
mainMenuRenderer.setSize(container.offsetWidth, container.offsetHeight);
mainMenuRenderer.setPixelRatio(window.devicePixelRatio);
mainMenuRenderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild(mainMenuRenderer.domElement);
mainMenuScene.add(new THREE.AmbientLight(0xffffff, 1.1));
const dl = new THREE.DirectionalLight(0xffffff, 0.5);
dl.position.set(10, 20, 15);
mainMenuScene.add(dl);
mainMenuPlayerGroup = new THREE.Group();
mainMenuScene.add(mainMenuPlayerGroup);
// Interaction for Main Menu Viewer
let prevX = 0;
container.addEventListener('mousedown', (e) => {
if (mainMenuSkinRenderMode !== '3d') return;
isMainSkinDragging = true;
prevX = e.clientX;
});
window.addEventListener('mouseup', () => isMainSkinDragging = false);
window.addEventListener('mousemove', (e) => {
if (mainMenuSkinRenderMode === '3d' && isMainSkinDragging && mainMenuPlayerGroup) {
mainMenuPlayerGroup.rotation.y += (e.clientX - prevX) * 0.01;
prevX = e.clientX;
}
});
// Auto-rotate slowly
function animateMain() {
requestAnimationFrame(animateMain);
if (mainMenuSkinRenderMode === '3d' && !isMainSkinDragging && mainMenuPlayerGroup) {
mainMenuPlayerGroup.rotation.y += 0.005;
}
if (mainMenuRenderer && mainMenuScene && mainMenuCamera) mainMenuRenderer.render(mainMenuScene, mainMenuCamera);
}
animateMain();
// Load current skin after short delay to ensure paths are ready
setTimeout(loadMainMenuSkin, 500);
// Handle window resize
window.addEventListener('resize', () => {
if (mainMenuCamera && mainMenuRenderer && container) {
mainMenuCamera.aspect = container.offsetWidth / container.offsetHeight;
mainMenuCamera.updateProjectionMatrix();
mainMenuRenderer.setSize(container.offsetWidth, container.offsetHeight);
}
});
}
async function loadMainMenuSkin() {
try {
// Ensure install dir is available (currentInstance might not be ready)
const installDir = await window.getInstallDir();
if (!installDir) return;
const skinPath = path.join(installDir, 'Common', 'res', 'mob', 'char.png');
if (fs.existsSync(skinPath)) {
const skinData = fs.readFileSync(skinPath);
const blob = new Blob([skinData]);
const url = URL.createObjectURL(blob);
const img = new Image();
img.onload = () => {
const isLegacy = img.height === 32;
updateSkinModel(img.src, isLegacy, mainMenuPlayerGroup, mainMenuSkinRenderMode);
};
img.src = url;
} else {
console.log("No skin found at " + skinPath);
}
} catch (e) {
console.warn("Could not load main menu skin (startup race condition?):", e);
}
}
function toggleMainSkinRenderMode() {
mainMenuSkinRenderMode = mainMenuSkinRenderMode === '3d' ? '2d' : '3d';
const modeButton = document.getElementById('btn-skin-render-mode');
if (modeButton) modeButton.textContent = mainMenuSkinRenderMode.toUpperCase();
const container = document.getElementById('main-skin-viewer');
if (container) {
container.style.cursor = mainMenuSkinRenderMode === '3d' ? 'grab' : 'default';
}
isMainSkinDragging = false;
if (mainMenuSkinRenderMode === '2d' && mainMenuPlayerGroup) {
mainMenuPlayerGroup.rotation.y = 0;
}
loadMainMenuSkin();
}
function updateSkinModel(dataUrl, isLegacy, targetGroup, renderMode = '3d') {
if (!targetGroup) return;
new THREE.TextureLoader().load(dataUrl, (texture) => {
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.NearestFilter;
texture.encoding = THREE.sRGBEncoding;
// Clear existing children
while(targetGroup.children.length > 0) targetGroup.remove(targetGroup.children[0]);
const createFaceMaterial = (tex, x, y, w, h) => {
const texWidth = tex.image.width;
const texHeight = tex.image.height;
const matTex = tex.clone();
matTex.magFilter = THREE.NearestFilter;
matTex.minFilter = THREE.NearestFilter;
matTex.repeat.set(w / texWidth, h / texHeight);
matTex.offset.set(x / texWidth, 1 - (y + h) / texHeight);
matTex.needsUpdate = true;
return new THREE.MeshLambertMaterial({ map: matTex, transparent: true, alphaTest: 0.5, side: THREE.FrontSide });
};
const createBodyPart = (w, h, d, tex, uv, offset = 0) => {
const geometry = new THREE.BoxGeometry(w, h, d);
const materials = [
createFaceMaterial(tex, uv.left[0], uv.left[1], uv.left[2], uv.left[3]), // Left (Standard MC "Right")
createFaceMaterial(tex, uv.right[0], uv.right[1], uv.right[2], uv.right[3]), // Right (Standard MC "Left")
createFaceMaterial(tex, uv.top[0], uv.top[1], uv.top[2], uv.top[3]),
createFaceMaterial(tex, uv.bottom[0], uv.bottom[1], uv.bottom[2], uv.bottom[3]),
createFaceMaterial(tex, uv.front[0], uv.front[1], uv.front[2], uv.front[3]),
createFaceMaterial(tex, uv.back[0], uv.back[1], uv.back[2], uv.back[3])
];
const mesh = new THREE.Mesh(geometry, materials);
if (offset !== 0) mesh.scale.set(1 + offset, 1 + offset, 1 + offset);
return mesh;
};
const limbUv = (x, y) => ({
top: [x+4, y, 4, 4], bottom: [x+8, y, 4, 4],
right: [x, y+4, 4, 12], front: [x+4, y+4, 4, 12],
left: [x+8, y+4, 4, 12], back: [x+12, y+4, 4, 12]
});
const create2DPart = (tex, uvFront, width, height, x, y, scale = 1) => {
const texWidth = tex.image.width;
const texHeight = tex.image.height;
const partTex = tex.clone();
partTex.magFilter = THREE.NearestFilter;
partTex.minFilter = THREE.NearestFilter;
partTex.repeat.set(uvFront[2] / texWidth, uvFront[3] / texHeight);
partTex.offset.set(uvFront[0] / texWidth, 1 - (uvFront[1] + uvFront[3]) / texHeight);
partTex.needsUpdate = true;
const geometry = new THREE.PlaneGeometry(width, height);
const material = new THREE.MeshBasicMaterial({ map: partTex, transparent: true, alphaTest: 0.5, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(x, y, 0);
mesh.scale.set(scale, scale, 1);
return mesh;
};
if (renderMode === '2d') {
const frontParts = [
create2DPart(texture, [8, 8, 8, 8], 8, 8, 0, 10),
create2DPart(texture, [40, 8, 8, 8], 8, 8, 0, 10, 1.12),
create2DPart(texture, [20, 20, 8, 12], 8, 12, 0, 0),
create2DPart(texture, [44, 20, 4, 12], 4, 12, -6, 0),
create2DPart(texture, isLegacy ? [44, 20, 4, 12] : [36, 52, 4, 12], 4, 12, 6, 0),
create2DPart(texture, [4, 20, 4, 12], 4, 12, -2, -12),
create2DPart(texture, isLegacy ? [4, 20, 4, 12] : [20, 52, 4, 12], 4, 12, 2, -12)
];
if (!isLegacy) {
frontParts.push(
create2DPart(texture, [20, 36, 8, 12], 8, 12, 0, 0, 1.05),
create2DPart(texture, [44, 36, 4, 12], 4, 12, -6, 0, 1.05),
create2DPart(texture, [52, 52, 4, 12], 4, 12, 6, 0, 1.05),
create2DPart(texture, [4, 36, 4, 12], 4, 12, -2, -12, 1.05),
create2DPart(texture, [4, 52, 4, 12], 4, 12, 2, -12, 1.05)
);
}
frontParts.forEach((part) => targetGroup.add(part));
targetGroup.rotation.y = 0;
return;
}
// Head
const headUvs = { top: [8, 0, 8, 8], bottom: [16, 0, 8, 8], right: [0, 8, 8, 8], left: [16, 8, 8, 8], front: [8, 8, 8, 8], back: [24, 8, 8, 8] };
const head = createBodyPart(8, 8, 8, texture, headUvs);
head.position.y = 10;
targetGroup.add(head);
// Hat
const hatUvs = { top: [40, 0, 8, 8], bottom: [48, 0, 8, 8], right: [32, 8, 8, 8], left: [48, 8, 8, 8], front: [40, 8, 8, 8], back: [56, 8, 8, 8] };
const hat = createBodyPart(8, 8, 8, texture, hatUvs, 0.12);
hat.position.y = 10;
targetGroup.add(hat);
// Torso
const torsoUvs = { top: [20, 16, 8, 4], bottom: [28, 16, 8, 4], right: [16, 20, 4, 12], left: [28, 20, 4, 12], front: [20, 20, 8, 12], back: [32, 20, 8, 12] };
targetGroup.add(createBodyPart(8, 12, 4, texture, torsoUvs));
// Jacket (non-legacy only)
if (!isLegacy) {
const jacketUvs = { top: [20, 32, 8, 4], bottom: [28, 32, 8, 4], right: [16, 36, 4, 12], left: [28, 36, 4, 12], front: [20, 36, 8, 12], back: [32, 36, 8, 12] };
targetGroup.add(createBodyPart(8, 12, 4, texture, jacketUvs, 0.05));
}
// Limbs
const limbs = [
{ pos: [-6, 0, 0], uv: limbUv(40, 16), layerUv: limbUv(40, 32) },
{ pos: [6, 0, 0], uv: isLegacy ? limbUv(40, 16) : limbUv(32, 48), layerUv: limbUv(48, 48) },
{ pos: [-2, -12, 0], uv: limbUv(0, 16), layerUv: limbUv(0, 32) },
{ pos: [2, -12, 0], uv: isLegacy ? limbUv(0, 16) : limbUv(16, 48), layerUv: limbUv(0, 48) }
];
limbs.forEach(l => {
const base = createBodyPart(4, 12, 4, texture, l.uv);
base.position.set(...l.pos);
targetGroup.add(base);
if (!isLegacy) {
const layer = createBodyPart(4, 12, 4, texture, l.layerUv, 0.05);
layer.position.set(...l.pos);
targetGroup.add(layer);
}
});
targetGroup.rotation.y = 0;
});
}
function openSkinManager() {
const modal = document.getElementById('skin-modal');
modal.style.display = 'flex';
modal.style.opacity = '1';
// Clear previous state in modal
const previewContainer = document.getElementById('preview-container');
if (previewContainer) previewContainer.classList.add('hidden');
const sysMsg = document.getElementById('sys-message');
if (sysMsg) sysMsg.classList.add('hidden');
// Load current skin into preview (optional, maybe we only want to see uploaded ones)
// loadCurrentSkinToPreview();
}
function closeSkinManager() {
const modal = document.getElementById('skin-modal');
modal.style.opacity = '0';
setTimeout(() => {
modal.style.display = 'none';
// Reset state
const prompt = document.getElementById('upload-prompt');
if (prompt) prompt.style.display = 'block';
const previewContainer = document.getElementById('preview-container');
if (previewContainer) previewContainer.classList.add('hidden');
processedSkinDataUrl = null;
}, 300);
}
function handleSkinFile(file) {
if (!file || !file.type.includes('png')) return window.showToast("Only PNG files are supported!");
const prompt = document.getElementById('upload-prompt');
if (prompt) prompt.style.display = 'none';
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
processSkinImage(img, e.target.result);
};
img.onerror = () => {
window.showToast("Failed to load image");
if(prompt) prompt.style.display = 'block';
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
function processSkinImage(img, srcUrl, isInitialLoad = false) {
const canvas = document.getElementById('skin-canvas');
const ctx = canvas.getContext('2d');
const formatLabel = document.getElementById('format-label');
const statusMessage = document.getElementById('status-message');
const previewContainer = document.getElementById('preview-container');
const saveBtn = document.getElementById('save-skin-btn');
const prompt = document.getElementById('upload-prompt');
if (img.width !== 64) {
if(prompt) prompt.style.display = 'block';
if(previewContainer) previewContainer.classList.add('hidden');
return window.showToast("Invalid skin width. Must be 64px.");
}
if(prompt) prompt.style.display = 'none';
const isLegacy = img.height === 32;
ctx.clearRect(0, 0, 64, 32);
ctx.drawImage(img, 0, 0, 64, 32, 0, 0, 64, 32);
processedSkinDataUrl = canvas.toDataURL('image/png');
previewContainer.classList.remove('hidden');
if (isInitialLoad) {
if(formatLabel) formatLabel.textContent = "Current Skin";
if(statusMessage) statusMessage.innerHTML = "<span class='text-blue-400 font-black' style='color: #60a5fa;'>LOADED FROM DISK</span>";
if(saveBtn) {
saveBtn.textContent = "SAVED";
saveBtn.classList.add('disabled');
}
} else {
if(formatLabel) formatLabel.textContent = isLegacy ? "64x32 (Legacy)" : "64x64 (Modern)";
if(statusMessage) statusMessage.innerHTML = isLegacy ? "<span class='text-green-400 font-black' style='color: #4ade80;'>LEGACY READY</span>" : "<span class='text-yellow-400 font-black' style='color: #facc15;'>CONVERTED TO 64x32</span>";
if(saveBtn) {
saveBtn.textContent = "SAVE SKIN";
saveBtn.classList.remove('disabled');
}
}
if (!skinScene) initPreviewEngine();
updateSkinModel(srcUrl, isLegacy, skinPlayerGroup, '3d');
}
function initPreviewEngine() {
const container = document.getElementById('skin-viewer-container');
if (!container) return;
skinScene = new THREE.Scene();
skinCamera = new THREE.PerspectiveCamera(35, container.offsetWidth / container.offsetHeight, 0.1, 1000);
skinCamera.position.set(0, 5, 70);
skinRenderer = new THREE.WebGLRenderer({ antialias: false, alpha: true });
skinRenderer.setSize(container.offsetWidth, container.offsetHeight);
skinRenderer.setPixelRatio(window.devicePixelRatio);
skinRenderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild(skinRenderer.domElement);
skinScene.add(new THREE.AmbientLight(0xffffff, 0.9));
const dl = new THREE.DirectionalLight(0xffffff, 0.35);
dl.position.set(10, 20, 15);
skinScene.add(dl);
skinPlayerGroup = new THREE.Group();
skinScene.add(skinPlayerGroup);
container.addEventListener('mousedown', () => isSkinDragging = true);
window.addEventListener('mouseup', () => isSkinDragging = false);
window.addEventListener('mousemove', (e) => {
if (isSkinDragging && skinPlayerGroup) {
skinPlayerGroup.rotation.y += (e.movementX) * 0.01;
}
});
function animate() {
requestAnimationFrame(animate);
if (!isSkinDragging && skinPlayerGroup) skinPlayerGroup.rotation.y += 0.008;
if (skinRenderer && skinScene && skinCamera) skinRenderer.render(skinScene, skinCamera);
}
animate();
}
async function saveSkinToDisk() {
if (!processedSkinDataUrl) return;
try {
const installDir = await window.getInstallDir();
const savePath = path.join(installDir, 'Common', 'res', 'mob', 'char.png');
const dir = path.dirname(savePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const base64Data = processedSkinDataUrl.replace(/^data:image\/png;base64,/, "");
fs.writeFileSync(savePath, base64Data, 'base64');
window.showToast("Skin Saved Successfully!");
const saveBtn = document.getElementById('save-skin-btn');
if(saveBtn) {
saveBtn.textContent = "SAVED!";
saveBtn.classList.add('disabled');
}
// Refresh main menu skin
loadMainMenuSkin();
// Close modal after short delay?
setTimeout(closeSkinManager, 1000);
} catch (e) {
window.showToast("Error Saving Skin: " + e.message);
console.error(e);
}
}
// Global Export
window.openSkinManager = openSkinManager;
window.initMainMenuSkinViewer = initMainMenuSkinViewer;
window.loadMainMenuSkin = loadMainMenuSkin;
window.toggleMainSkinRenderMode = toggleMainSkinRenderMode;

View file

@ -1,483 +0,0 @@
Happy birthday, ez!
Happy birthday, Notch!
Let me tell you about Homestuck!
Let me tell you about Homestuck!
Let me tell you about Homestuck!
Merry X-mas!
Happy New Year!
As seen on TV!
A young man stands in his bedroom!
A young man stands in his bedroom!
A young woman stands in her bedroom!
A young woman stands in her bedroom!
Awesome!
100% pure!
Better than Prey!
More polygons!
Get the cruxite dowel!
Get the cruxite dowel!
Get the cruxite dowel!
Flashing letters!
Made by Notch!
It's here!
Best in class!
It's finished!
Kind of dragon free!
Excitement!
Where doing it man, where making this hapen!
Where doing it man, where making this hapen!
One of a kind!
Heaps of hits on YouTube!
Indev!
Spiders everywhere!
Check it out!
Holy cow, man!
It's a game!
Made in Sweden!
Uses LWJGL!
Reticulating splines!
Minecraft!
Yaaay!
413!
413!
413!
413!
Singleplayer!
Keyboard compatible!
Undocumented!
Ingots!
Exploding creepers!
That's no moon!
l33t!
Create!
Survive!
Dungeon!
I WARNED YOU ABOUT STAIRS BRO!!!!
I WARNED YOU ABOUT STAIRS BRO!!!!
I WARNED YOU ABOUT STAIRS BRO!!!!
Exclusive!
The bee's knees!
Closed source!
Classy!
Wow!
Not on steam!
Oh man!
Awesome community!
Pixels!
Teetsuuuuoooo!
Kaaneeeedaaaa!
Now with difficulty!
Enhanced!
90% bug free!
Pretty!
12 herbs and spices!
Fat free!
Absolutely no memes!
Free dental!
Cloud computing!
Hard to label!
Technically good!
Bringing home the bacon!
Indie!
GOTY!
Ceci n'est pas une title screen!
Euclidian!
Now in 3D!
Inspirational!
Herregud!
Complex cellular automata!
Yes, sir!
Played by cowboys!
Thousands of colors!
Try it!
Age of Wonders is better!
Try the mushroom stew!
Sensational!
Hot tamale, hot hot tamale!
Play him off, keyboard cat!
Guaranteed!
Alchemize ALL the things!
Alchemize ALL the things!
Macroscopic!
Bring it on!
Random splash!
Call your mother!
Monster infighting!
Loved by millions!
Ultimate edition!
Freaky!
Water proof!
Uninflammable!
Whoa, dude!
Roxy is best hacker!
Roxy is best hacker!
Roxy is best hacker!
All inclusive!
Tell your friends!
NP is not in P!
Notch <3 ez!
Music by C418!
Livestreamed!
Haunted!
Polynomial!
Terrestrial!
All is full of love!
Full of stars!
Scientific!
Cooler than Spock!
Collaborate and listen!
Never dig down!
Take frequent breaks!
Not linear!
Han shot first!
Nice to meet you!
Buckets of lava!
Ride the pig!
Larger than Earth!
sqrt(-1) love you!
Phobos anomaly!
Punching wood!
Falling off cliffs!
0% sugar!
150% hyperbole!
Synecdoche!
Let's danec!
Reference implementation!
Lewd with two dudes with food!
Kiss the sky!
20 GOTO 10!
Verlet intregration!
Peter Griffin!
Do not distribute!
Cogito ergo sum!
4815162342 lines of code!
A skeleton popped out!
The Work of Notch!
The sum of its parts!
BTAF used to be good!
I miss ADOM!
umop-apisdn!
OICU812!
Bring me Ray Cokes!
Finger-licking!
Thematic!
Pneumatic!
Sublime!
Octagonal!
Une baguette!
Gargamel plays it!
Rita is the new top dog!
SWM forever!
Representing Edsbyn!
Matt Damon!
Supercalifragilisticexpialidocious!
Consummate V's!
Cow Tools!
Double buffered!
Fan fiction!
Flaxkikare!
Jason! Jason! Jason!
Hotter than the sun!
Internet enabled!
Autonomous!
Engage!
Fantasy!
DRR! DRR! DRR!
Kick it root down!
Regional resources!
Woo, facepunch!
Woo, somethingawful!
Woo, /v/!
Woo, tigsource!
Woo, minecraftforum!
Woo, worldofminecraft!
Woo, reddit!
Woo, 2pp!
Google anlyticsed!
Now supports åäö!
Give us Gordon!
Tip your waiter!
Very fun!
12345 is a bad password!
Vote for net neutrality!
Lives in a pineapple under the sea!
MAP11 has two names!
Omnipotent!
Gasp!
...!
Bees, bees, bees, bees!
Jag känner en bot!
This text is hard to read if you play the game at the default resolution, but at 1080p it's fine!
Haha, LOL!
Hampsterdance!
Switches and ores!
Menger sponge!
idspispopd!
Eple (original edit)!
So fresh, so clean!
Slow acting portals!
Try the Nether!
Don't look directly at the bugs!
Oh, ok, Pigmen!
Finally with ladders!
Scary!
Play Minecraft, Watch Topgear, Get Pig!
Twittered about!
Jump up, jump up, and get down!
Joel is neat!
A riddle, wrapped in a mystery!
Huge tracts of land!
Welcome to your Doom!
Stay a while, stay forever!
Stay a while and listen!
Treatment for your rash!
"Autological" is!
Information wants to be free!
"Almost never" is an interesting concept!
Lots of truthiness!
The creeper is a spy!
Turing complete!
It's groundbreaking!
Let our battle's begin!
The sky is the limit!
Jeb has amazing hair!
Casual gaming!
Undefeated!
Kinda like Lemmings!
Follow the train, CJ!
Leveraging synergy!
This message will never appear on the splash screen, isn't that weird?
DungeonQuest is unfair!
110813!
90210!
Check out the far lands!
Tyrion would love it!
Also try VVVVVV!
Also try Super Meat Boy!
Also try Terraria!
Also try Mount And Blade!
Also try Project Zomboid!
Also try World of Goo!
Also try Limbo!
Also try Pixeljunk Shooter!
Also try Braid!
Also try Sburb!
Also try Sburb!
Also try Sburb!
That's super!
Bread is pain!
Read more books!
Khaaaaaaaaan!
Less addictive than TV Tropes!
More addictive than lemonade!
Bigger than a bread box!
Millions of peaches!
Fnord!
This is my true form!
Totally forgot about Dre!
Don't bother with the clones!
Pumpkinhead!
Hobo humping slobo babe!
Made by Jeb!
Has an ending!
Finally complete!
Feature packed!
Boots with the fur!
Stop, hammertime!
Testificates!
Conventional!
Homeomorphic to a 3-sphere!
Doesn't avoid double negatives!
Place ALL the blocks!
Does barrel rolls!
Meeting expectations!
PC gaming since 1873!
Ghoughpteighbteau tchoghs!
Déjà vu!
IT KEEPS HAPPENING!
IT KEEPS HAPPENING!
IT KEEPS HAPPENING!
I TOLD YOU DOG!
I TOLD YOU DOG!
Déjà vu!
Got your nose!
Afraid of the big, black bat!
Doesn't use the U-word!
Child's play!
See you next Friday or so!
From the streets of Södermalm!
150 bpm for 400000 minutes!
Technologic!
Funk soul brother!
Pumpa kungen!
Fetchstuck compatible!
Sylladex full!
Sylladex full!
Honk. HONK.
Honk. HONK.
You can't fight the Homestuck!
You can't fight the Homestuck!
==> Enter.
==> Enter.
STRIFE!
STRIFE!
8^y
8^y
Sweet Bro and Hella Jeff!
What pumpkin?
What pumpkin?
ABSCOND!
ABSCOND!
Check your Strife Specibus!
Ascend to God Tier!
Ascend to God Tier!
Got tiger!
Got tiger!
Motherfucking miracles!
Welcome to Skaia!
Welcome to Skaia!
Prospit or Derse?
Upgrading the Alchemiter!
Build Grist!
Build Grist!
Now with 100% more Faygo!
Sopor slime pie!
Grimdark!
Trickster mode engaged!
How HIGH do you even have to BE just to DO something like that........
Mayor of Can Town!
Spades Slick did nothing wrong!
It's hard and nobody understands.
Shenanigans!
The Scratch is happening!
612!
111111!
Jegus!
All the luck!
All the luck!
Dunkass!
Captchalogue this!
Captchalogue this!
Echeladder rung attained!
Megalovania originally played here!
Megalovania originally played here!
Music by Toby Fox!
Don't ask about the buckets!
Don't ask about the buckets!
Candy corn horns!
Your name is JOHN EGBERT.
Let me guess, your zodiac sign is a troll?
Put the bunny back in the box!
Do the windy thing!
Do the windy thing!
Betty Crocker is evil!
Betty Crocker is evil!
Beware the Batterwitch!
He is already here.
He is already here.
Doc Scratch is watching.
Con Air is a masterpiece!
Karkalicious!
SGRUB!
Sburb Beta!
Sburb Beta!
Knight of Time!
Seer of Light!
Heir of Breath!
Witch of Space!
Flipping the frog switch!
Forge the Bilious Sliver!
Midnight Crew!
We have the cue ball!
th1s 1s r3d1culous.
wweh...
glub glub...
DAVE: this is stupid
WHAT THE FUCK IS A CREEPER.
I AM GOING TO THROW A TANTRUM.
H3H3H3! 1 C4N SM3LL TH3 D14MOND 0R3!
T4ST3S L1K3 CH3RRY!
uHH, pLEASE DONT MINE THAT,,, iTS MINE.
ii jju2t want two play a game.
two much iinformatiion.
:33 < ac goes on a purrfect mining trip!
:33 < furrealsies!
I Cannot Believe You Just Mined That Dirt.
Please Stop Being Ridiculous.
All 8 of my 8lue diamonds! ::::::::)
You're w8ing for something?
D --> That block is insufficiently STRONG
D --> e%cuse me
tHaT sHiT iS mOtHeRfUcKiNg MiRaCuLoUs.
mY mOtHeRfUcKiNg BrOtHeR.
wwhats the point of minin anywway.
38) This is so GLUBBING exciting!
the dead p0pulati0n is b0thering me.
0k. i am t0tally fine with this.
ROXY: wonk ;)
ROXY: obfuscation!!!
ROXY: i am the best hacker
ROXY: meow
ROXY: dirk u there
ROXY: i am going to save u
ROXY: wtf is this block
ROXY: where r the fucking cats
ROXY: im kinda drunk
ROXY: the magic is real
ROXY: sweet catch!
ROXY: frigglish!
ROXY: where are all the wizards
ROXY: im putting on my god tier pjs
ROXY: pumpkins r legally outlawed
ROXY: mutieeeee
ROXY: cant wake up
ROXY: sup
ROXY: wonk ;)
JOHN: wow, okay.
JOHN: wow, okay.
JOHN: i am a true prankster!
JOHN: what are you even talking about?
JOHN: let's go do some ghostbusting!
JOHN: i'm not a homosexual!
JOHN: i am a true prankster!
Prankster's Gambit!
Prankster's Gambit!
Ectobiology!
Ectobiology!
JADE: hooray!!!
JADE: <3 <3 <3
JADE: beep beep meow
JADE: im going to build a tall tower!
JADE: hooray!!!
JADE: <3 <3 <3
Becquerel is a good boy!
Becquerel is a good boy!
Good dog. Best friend.
Good dog. Best friend.
Squiddleknit!
Squiddles!
DAVE: i am not a hero
DAVE: ironically of course
DAVE: time shenanigans
DAVE: lets drop it like its hot
DAVE: apple juice
DAVE: sweet catch
ROSE: Let the record state that I am not a fan of this.
ROSE: I will tear this game apart.
ROSE: The plot thickens.
ROSE: We are playing a game.
Pony Pals!
DIRK: You can't escape the miles.
DIRK: Sup.
DIRK: im going to build a robot.
Auto-Responder!
JAKE: Tally ho!
JAKE: Pip pip!
JAKE: By jove!
JAKE: Egads!
JAKE: Jolly good show
Jade Harley!!!
Jade Harley!!!
Jade Harley!!!
Jade Harley!!!
Jade Harley!!!

1424
style.css

File diff suppressed because it is too large Load diff