mirror of
https://git.sanhost.net/sanasol/hytale-f2p
synced 2026-02-26 17:31:48 -03:00
* Add electron-updater auto-update support - Install electron-updater package - Configure GitHub releases publish settings - Create AppUpdater class with full update lifecycle - Integrate auto-update into main.js - Add comprehensive documentation (AUTO-UPDATES.md, TESTING-UPDATES.md) - Set up dev-app-update.yml for testing * Add cache clearing documentation for electron-updater - Introduced CLEAR-UPDATE-CACHE.md to guide users on clearing the electron-updater cache across macOS, Windows, and Linux. - Added programmatic method for cache clearing in JavaScript. - Enhanced update handling in main.js and preload.js to support new update events. - Updated GUI styles for download buttons and progress indicators in update.js and style.css. * Update auto-update UI and configuration - Fix version display (newVersion field) - Add download progress bar with real-time updates - Reorder buttons: Install & Restart (primary), Manually Download (secondary) - Update dev-app-update.yml to point to fork - Update package.json version to 2.0.2 * Add installation effects and draggable progress bar Introduces animated installation effects overlay and makes the progress bar draggable. Adds maximize window support, improves window controls styling, and enforces a single app instance. Removes the unused Skins page and related translations. Refines various UI details for a more polished user experience. * Adjust news card aspect ratio and add Play tab style Set a default aspect ratio for .news-card and add a specific style for the LATEST NEWS section in the Play tab to override the aspect ratio and use full height. * Add splash screen to launcher startup Introduced a new splash screen (splash.html) and updated main.js to display it on startup before loading the main window. The splash screen is shown for 2.5 seconds as a placeholder for future loading logic, improving user experience during application launch. * Display launcher version in UI Adds a version display element to the bottom right of the UI, fetching the version from package.json via a new IPC handler. Updates main.js, preload.js, and ui.js to support retrieving and displaying the version, and adds relevant styles in style.css. * Custom Mod loading fix (#92) * feat: Add Repair Game functionality including UserData backup and cache clearing * feat: Add In-App Logs Viewer and Logs Folder shortcut * feat: Add Open Logs feature * disable dev tools * Fix Settings UI * Implement custom mod loading, autoimport, auto repair * Fixed Custom Mod loading issues and merge issues * feat: Externalize sensitive API keys and Discord client ID into environment variables using dotenv. * feat(mods): add profile-based mod management and auto-repair * feat: add 'Close launcher on game start' option and improve app termination behavior (#93) * update main branch to release/v2.0.2b (#86) * add more linux pkgs, create auto-release and pre-release feature for Github Actions * removed package-lock from gitignore * update .gitignore for local build * add package-lock.json to maintain stability development * update version to 2.0.2b also add deps for rpm and arch * update 2.0.2b: add arm64 support, product and executable name, maintainers; remove snap; * update 2.0.2b: add latest.yml for win & linux, arm64 support; remove snap * fix release build naming * Prepare release v2.0.2b * feat: add 'Close launcher on game start' option and improve app termination behavior - Added 'Close launcher on game start' setting in GUI and backend. - Implemented automatic app quit after game launch if setting is enabled. - Added Cmd+Q (Mac) and Ctrl+Q/Alt+F4 (Win/Linux) shortcuts to quit the app. - Updated 'window-close' handler to fully quit the app instead of just closing the window. - Added i18n support for the new setting in English, Spanish, and Portuguese. --------- Co-authored-by: Fazri Gading <fazrigading@gmail.com> Co-authored-by: Arnav Singh <hi.arnavsingh3@gmail.com> * Update publish config to point to chasem-dev fork * Fix Linux metadata files in workflow and improve error handling * Bump version to 2.0.5 * Bump version to 2.0.6 * Fix update popup showing for same version - add version comparison checks * Bump version to 2.0.7 * Fix SHA512 checksum mismatch handling - clear cache and retry automatically * Bump version to 2.0.8 * Bump version to 2.0.9 * Fix: Use explicit latest-linux.yml to prevent yml file collision The glob pattern latest*.yml was matching both latest-linux.yml AND latest.yml from the Linux build, causing the Windows latest.yml to be overwritten with incorrect checksums. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Bump version to 2.0.10 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix: Remove portable target to fix SHA512 checksum mismatch The portable and nsis targets both produced x64.exe files with the same name, causing one to overwrite the other. The latest.yml contained the checksum from one build while the actual file was from the other build. Removed portable target - nsis installer is sufficient. Bump version to 2.0.11 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Remove outdated documentation files related to auto-updates, build instructions, and testing updates. Update `dev-app-update.yml` and `package.json` to reflect the correct GitHub owner. This cleanup streamlines the project and ensures accurate configuration for future updates. * Add semantic versioning policy documentation - numerical versions only * Update package-lock.json to include new dependencies and versions, enhancing project stability and compatibility. * fixed imgur restriction for UK * fix: adds EGL env var to detect installed NVIDIA GPU * Update release.yml * patch v2.0.11-beta: fix env issue in GA release, warn Intel Mac users, add com templates. (#115) * fix: throw error for Intel Mac user * docs: first draft of issue and PR template * fix: env of curseforge API key and discord client ID * implemented late patch should be in #115 * Final patch for release.yml v2.0.11 --------- Co-authored-by: chasem-dev <myers.a.chase@gmail.com> Co-authored-by: AMIAY <letudiantenrap.collab@gmail.com> Co-authored-by: Rahul Sahani <110347707+Rahul-Sahani04@users.noreply.github.com> Co-authored-by: Arnav Singh <72737311+ArnavSingh77@users.noreply.github.com> Co-authored-by: Arnav Singh <hi.arnavsingh3@gmail.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
894 lines
29 KiB
JavaScript
894 lines
29 KiB
JavaScript
|
|
let customJavaCheck;
|
|
let customJavaOptions;
|
|
let customJavaPath;
|
|
let browseJavaBtn;
|
|
let settingsPlayerName;
|
|
let discordRPCCheck;
|
|
let closeLauncherCheck;
|
|
let gpuPreferenceRadios;
|
|
|
|
|
|
// UUID Management elements
|
|
let currentUuidDisplay;
|
|
let copyUuidBtn;
|
|
let regenerateUuidBtn;
|
|
let manageUuidsBtn;
|
|
let uuidModal;
|
|
let uuidModalClose;
|
|
let modalCurrentUuid;
|
|
let modalCopyUuidBtn;
|
|
let modalRegenerateUuidBtn;
|
|
let generateNewUuidBtn;
|
|
let uuidList;
|
|
let customUuidInput;
|
|
let setCustomUuidBtn;
|
|
|
|
function showCustomConfirm(message, title, onConfirm, onCancel = null, confirmText, cancelText) {
|
|
// Apply defaults with i18n support
|
|
title = title || (window.i18n ? window.i18n.t('confirm.defaultTitle') : 'Confirm Action');
|
|
confirmText = confirmText || (window.i18n ? window.i18n.t('common.confirm') : 'Confirm');
|
|
cancelText = cancelText || (window.i18n ? window.i18n.t('common.cancel') : 'Cancel');
|
|
|
|
const existingModal = document.querySelector('.custom-confirm-modal');
|
|
if (existingModal) {
|
|
existingModal.remove();
|
|
}
|
|
|
|
const modal = document.createElement('div');
|
|
modal.className = 'custom-confirm-modal';
|
|
modal.style.cssText = `
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
backdrop-filter: blur(4px);
|
|
z-index: 20000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
`;
|
|
|
|
const dialog = document.createElement('div');
|
|
dialog.className = 'custom-confirm-dialog';
|
|
dialog.style.cssText = `
|
|
background: #1f2937;
|
|
border-radius: 12px;
|
|
padding: 0;
|
|
min-width: 400px;
|
|
max-width: 500px;
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6);
|
|
border: 1px solid rgba(147, 51, 234, 0.3);
|
|
transform: scale(0.9);
|
|
transition: transform 0.3s ease;
|
|
`;
|
|
|
|
dialog.innerHTML = `
|
|
<div style="padding: 24px; border-bottom: 1px solid rgba(255,255,255,0.1);">
|
|
<div style="display: flex; align-items: center; gap: 12px; color: #9333ea;">
|
|
<i class="fas fa-exclamation-triangle" style="font-size: 24px;"></i>
|
|
<h3 style="margin: 0; font-size: 1.2rem; font-weight: 600;">${title}</h3>
|
|
</div>
|
|
</div>
|
|
<div style="padding: 24px; color: #e5e7eb;">
|
|
<p style="margin: 0; line-height: 1.5; font-size: 1rem;">${message}</p>
|
|
</div>
|
|
<div style="padding: 20px 24px; display: flex; gap: 12px; justify-content: flex-end; border-top: 1px solid rgba(255,255,255,0.1);">
|
|
<button class="custom-confirm-cancel" style="
|
|
background: transparent;
|
|
color: #9ca3af;
|
|
border: 1px solid rgba(156, 163, 175, 0.3);
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
transition: all 0.2s;
|
|
">${cancelText}</button>
|
|
<button class="custom-confirm-action" style="
|
|
background: linear-gradient(135deg, #9333ea, #3b82f6);
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
transition: all 0.2s;
|
|
">${confirmText}</button>
|
|
</div>
|
|
`;
|
|
|
|
modal.appendChild(dialog);
|
|
document.body.appendChild(modal);
|
|
|
|
// Animate in
|
|
setTimeout(() => {
|
|
modal.style.opacity = '1';
|
|
dialog.style.transform = 'scale(1)';
|
|
}, 10);
|
|
|
|
// Event handlers
|
|
const cancelBtn = dialog.querySelector('.custom-confirm-cancel');
|
|
const actionBtn = dialog.querySelector('.custom-confirm-action');
|
|
|
|
const closeModal = () => {
|
|
modal.style.opacity = '0';
|
|
dialog.style.transform = 'scale(0.9)';
|
|
setTimeout(() => {
|
|
modal.remove();
|
|
}, 300);
|
|
};
|
|
|
|
cancelBtn.onclick = () => {
|
|
closeModal();
|
|
if (onCancel) onCancel();
|
|
};
|
|
|
|
actionBtn.onclick = () => {
|
|
closeModal();
|
|
onConfirm();
|
|
};
|
|
|
|
modal.onclick = (e) => {
|
|
if (e.target === modal) {
|
|
closeModal();
|
|
if (onCancel) onCancel();
|
|
}
|
|
};
|
|
|
|
// Escape key
|
|
const handleEscape = (e) => {
|
|
if (e.key === 'Escape') {
|
|
closeModal();
|
|
if (onCancel) onCancel();
|
|
document.removeEventListener('keydown', handleEscape);
|
|
}
|
|
};
|
|
document.addEventListener('keydown', handleEscape);
|
|
}
|
|
|
|
|
|
export function initSettings() {
|
|
setupSettingsElements();
|
|
loadAllSettings();
|
|
}
|
|
|
|
function setupSettingsElements() {
|
|
customJavaCheck = document.getElementById('customJavaCheck');
|
|
customJavaOptions = document.getElementById('customJavaOptions');
|
|
customJavaPath = document.getElementById('customJavaPath');
|
|
browseJavaBtn = document.getElementById('browseJavaBtn');
|
|
settingsPlayerName = document.getElementById('settingsPlayerName');
|
|
discordRPCCheck = document.getElementById('discordRPCCheck');
|
|
closeLauncherCheck = document.getElementById('closeLauncherCheck');
|
|
gpuPreferenceRadios = document.querySelectorAll('input[name="gpuPreference"]');
|
|
|
|
|
|
// UUID Management elements
|
|
currentUuidDisplay = document.getElementById('currentUuid');
|
|
copyUuidBtn = document.getElementById('copyUuidBtn');
|
|
regenerateUuidBtn = document.getElementById('regenerateUuidBtn');
|
|
manageUuidsBtn = document.getElementById('manageUuidsBtn');
|
|
uuidModal = document.getElementById('uuidModal');
|
|
uuidModalClose = document.getElementById('uuidModalClose');
|
|
modalCurrentUuid = document.getElementById('modalCurrentUuid');
|
|
modalCopyUuidBtn = document.getElementById('modalCopyUuidBtn');
|
|
modalRegenerateUuidBtn = document.getElementById('modalRegenerateUuidBtn');
|
|
generateNewUuidBtn = document.getElementById('generateNewUuidBtn');
|
|
uuidList = document.getElementById('uuidList');
|
|
customUuidInput = document.getElementById('customUuidInput');
|
|
setCustomUuidBtn = document.getElementById('setCustomUuidBtn');
|
|
|
|
if (customJavaCheck) {
|
|
customJavaCheck.addEventListener('change', toggleCustomJava);
|
|
}
|
|
|
|
if (browseJavaBtn) {
|
|
browseJavaBtn.addEventListener('click', browseJavaPath);
|
|
}
|
|
|
|
if (settingsPlayerName) {
|
|
settingsPlayerName.addEventListener('change', savePlayerName);
|
|
}
|
|
|
|
if (discordRPCCheck) {
|
|
discordRPCCheck.addEventListener('change', saveDiscordRPC);
|
|
}
|
|
|
|
if (closeLauncherCheck) {
|
|
closeLauncherCheck.addEventListener('change', saveCloseLauncher);
|
|
}
|
|
|
|
|
|
// UUID event listeners
|
|
if (copyUuidBtn) {
|
|
copyUuidBtn.addEventListener('click', copyCurrentUuid);
|
|
}
|
|
|
|
if (regenerateUuidBtn) {
|
|
regenerateUuidBtn.addEventListener('click', regenerateCurrentUuid);
|
|
}
|
|
|
|
if (manageUuidsBtn) {
|
|
manageUuidsBtn.addEventListener('click', openUuidModal);
|
|
}
|
|
|
|
if (uuidModalClose) {
|
|
uuidModalClose.addEventListener('click', closeUuidModal);
|
|
}
|
|
|
|
if (modalCopyUuidBtn) {
|
|
modalCopyUuidBtn.addEventListener('click', copyCurrentUuid);
|
|
}
|
|
|
|
if (modalRegenerateUuidBtn) {
|
|
modalRegenerateUuidBtn.addEventListener('click', regenerateCurrentUuid);
|
|
}
|
|
|
|
if (generateNewUuidBtn) {
|
|
generateNewUuidBtn.addEventListener('click', generateNewUuid);
|
|
}
|
|
|
|
if (setCustomUuidBtn) {
|
|
setCustomUuidBtn.addEventListener('click', setCustomUuid);
|
|
}
|
|
|
|
if (uuidModal) {
|
|
uuidModal.addEventListener('click', (e) => {
|
|
if (e.target === uuidModal) {
|
|
closeUuidModal();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (gpuPreferenceRadios) {
|
|
gpuPreferenceRadios.forEach(radio => {
|
|
radio.addEventListener('change', async () => {
|
|
await saveGpuPreference();
|
|
await updateGpuLabel();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
function toggleCustomJava() {
|
|
if (!customJavaOptions) return;
|
|
|
|
if (customJavaCheck && customJavaCheck.checked) {
|
|
customJavaOptions.style.display = 'block';
|
|
} else {
|
|
customJavaOptions.style.display = 'none';
|
|
if (customJavaPath) customJavaPath.value = '';
|
|
saveCustomJavaPath('');
|
|
}
|
|
}
|
|
|
|
async function browseJavaPath() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.browseJavaPath) {
|
|
const result = await window.electronAPI.browseJavaPath();
|
|
if (result && result.filePaths && result.filePaths.length > 0) {
|
|
const selectedPath = result.filePaths[0];
|
|
if (customJavaPath) {
|
|
customJavaPath.value = selectedPath;
|
|
}
|
|
await saveCustomJavaPath(selectedPath);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error browsing Java path:', error);
|
|
}
|
|
}
|
|
|
|
async function saveCustomJavaPath(path) {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.saveJavaPath) {
|
|
await window.electronAPI.saveJavaPath(path);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving custom Java path:', error);
|
|
}
|
|
}
|
|
|
|
async function loadCustomJavaPath() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.loadJavaPath) {
|
|
const savedPath = await window.electronAPI.loadJavaPath();
|
|
if (savedPath && savedPath.trim()) {
|
|
if (customJavaPath) {
|
|
customJavaPath.value = savedPath;
|
|
}
|
|
if (customJavaCheck) {
|
|
customJavaCheck.checked = true;
|
|
}
|
|
if (customJavaOptions) {
|
|
customJavaOptions.style.display = 'block';
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading custom Java path:', error);
|
|
}
|
|
}
|
|
|
|
async function saveDiscordRPC() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.saveDiscordRPC && discordRPCCheck) {
|
|
const enabled = discordRPCCheck.checked;
|
|
console.log('Saving Discord RPC setting:', enabled);
|
|
|
|
const result = await window.electronAPI.saveDiscordRPC(enabled);
|
|
|
|
if (result && result.success) {
|
|
console.log('Discord RPC setting saved successfully:', enabled);
|
|
|
|
// Feedback visuel pour l'utilisateur
|
|
if (enabled) {
|
|
const msg = window.i18n ? window.i18n.t('notifications.discordEnabled') : 'Discord Rich Presence enabled';
|
|
showNotification(msg, 'success');
|
|
} else {
|
|
const msg = window.i18n ? window.i18n.t('notifications.discordDisabled') : 'Discord Rich Presence disabled';
|
|
showNotification(msg, 'success');
|
|
}
|
|
} else {
|
|
throw new Error('Failed to save Discord RPC setting');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving Discord RPC setting:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.discordSaveFailed') : 'Failed to save Discord setting';
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
async function loadDiscordRPC() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.loadDiscordRPC) {
|
|
const enabled = await window.electronAPI.loadDiscordRPC();
|
|
if (discordRPCCheck) {
|
|
discordRPCCheck.checked = enabled;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading Discord RPC setting:', error);
|
|
}
|
|
}
|
|
|
|
async function saveCloseLauncher() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.saveCloseLauncher && closeLauncherCheck) {
|
|
const enabled = closeLauncherCheck.checked;
|
|
await window.electronAPI.saveCloseLauncher(enabled);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving close launcher setting:', error);
|
|
}
|
|
}
|
|
|
|
async function loadCloseLauncher() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.loadCloseLauncher) {
|
|
const enabled = await window.electronAPI.loadCloseLauncher();
|
|
if (closeLauncherCheck) {
|
|
closeLauncherCheck.checked = enabled;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading close launcher setting:', error);
|
|
}
|
|
}
|
|
|
|
|
|
async function savePlayerName() {
|
|
try {
|
|
if (!window.electronAPI || !settingsPlayerName) return;
|
|
|
|
const playerName = settingsPlayerName.value.trim();
|
|
|
|
if (!playerName) {
|
|
const msg = window.i18n ? window.i18n.t('notifications.playerNameRequired') : 'Please enter a valid player name';
|
|
showNotification(msg, 'error');
|
|
return;
|
|
}
|
|
|
|
await window.electronAPI.saveUsername(playerName);
|
|
const successMsg = window.i18n ? window.i18n.t('notifications.playerNameSaved') : 'Player name saved successfully';
|
|
showNotification(successMsg, 'success');
|
|
|
|
} catch (error) {
|
|
console.error('Error saving player name:', error);
|
|
const errorMsg = window.i18n ? window.i18n.t('notifications.playerNameSaveFailed') : 'Failed to save player name';
|
|
showNotification(errorMsg, 'error');
|
|
}
|
|
}
|
|
|
|
async function loadPlayerName() {
|
|
try {
|
|
if (!window.electronAPI || !settingsPlayerName) return;
|
|
|
|
const savedName = await window.electronAPI.loadUsername();
|
|
if (savedName) {
|
|
settingsPlayerName.value = savedName;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading player name:', error);
|
|
}
|
|
}
|
|
|
|
async function saveGpuPreference() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.saveGpuPreference && gpuPreferenceRadios) {
|
|
const gpuPreference = Array.from(gpuPreferenceRadios).find(radio => radio.checked)?.value || 'auto';
|
|
await window.electronAPI.saveGpuPreference(gpuPreference);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving GPU preference:', error);
|
|
}
|
|
}
|
|
|
|
async function updateGpuLabel() {
|
|
const detectionInfo = document.getElementById('gpu-detection-info');
|
|
if (!detectionInfo) return;
|
|
|
|
if (gpuPreferenceRadios) {
|
|
const checked = Array.from(gpuPreferenceRadios).find(radio => radio.checked);
|
|
if (checked) {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.getDetectedGpu) {
|
|
const detected = await window.electronAPI.getDetectedGpu();
|
|
if (checked.value === 'auto') {
|
|
if (detected.dedicatedName) {
|
|
detectionInfo.textContent = `dGPU detected, using ${detected.dedicatedName}`;
|
|
} else {
|
|
detectionInfo.textContent = `dGPU not detected, using iGPU (${detected.integratedName}) instead`;
|
|
}
|
|
detectionInfo.style.display = 'block';
|
|
} else if (checked.value === 'integrated') {
|
|
detectionInfo.textContent = `Detected: ${detected.integratedName}`;
|
|
detectionInfo.style.display = 'block';
|
|
} else if (checked.value === 'dedicated') {
|
|
if (detected.dedicatedName) {
|
|
detectionInfo.textContent = `Detected: ${detected.dedicatedName}`;
|
|
} else {
|
|
detectionInfo.textContent = `No dedicated GPU detected`;
|
|
}
|
|
detectionInfo.style.display = 'block';
|
|
} else {
|
|
detectionInfo.style.display = 'none';
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error getting detected GPU:', error);
|
|
detectionInfo.style.display = 'none';
|
|
}
|
|
} else {
|
|
detectionInfo.style.display = 'none';
|
|
}
|
|
} else {
|
|
detectionInfo.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
async function loadGpuPreference() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.loadGpuPreference && gpuPreferenceRadios) {
|
|
const savedPreference = await window.electronAPI.loadGpuPreference();
|
|
if (savedPreference) {
|
|
for (const radio of gpuPreferenceRadios) {
|
|
if (radio.value === savedPreference) {
|
|
radio.checked = true;
|
|
break;
|
|
}
|
|
}
|
|
await updateGpuLabel();
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading GPU preference:', error);
|
|
}
|
|
}
|
|
|
|
async function loadAllSettings() {
|
|
await loadCustomJavaPath();
|
|
await loadPlayerName();
|
|
await loadCurrentUuid();
|
|
await loadDiscordRPC();
|
|
await loadCloseLauncher();
|
|
await loadGpuPreference();
|
|
}
|
|
|
|
|
|
async function openGameLocation() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.openGameLocation) {
|
|
await window.electronAPI.openGameLocation();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error opening game location:', error);
|
|
}
|
|
}
|
|
|
|
export function getCurrentJavaPath() {
|
|
if (customJavaCheck && customJavaCheck.checked && customJavaPath) {
|
|
return customJavaPath.value.trim();
|
|
}
|
|
return '';
|
|
}
|
|
|
|
|
|
export function getCurrentPlayerName() {
|
|
if (settingsPlayerName && settingsPlayerName.value.trim()) {
|
|
return settingsPlayerName.value.trim();
|
|
}
|
|
return 'Player';
|
|
}
|
|
|
|
window.openGameLocation = openGameLocation;
|
|
|
|
document.addEventListener('DOMContentLoaded', initSettings);
|
|
|
|
window.SettingsAPI = {
|
|
getCurrentJavaPath,
|
|
getCurrentPlayerName
|
|
};
|
|
|
|
async function loadCurrentUuid() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.getCurrentUuid) {
|
|
const uuid = await window.electronAPI.getCurrentUuid();
|
|
if (uuid) {
|
|
if (currentUuidDisplay) currentUuidDisplay.value = uuid;
|
|
if (modalCurrentUuid) modalCurrentUuid.value = uuid;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading current UUID:', error);
|
|
}
|
|
}
|
|
|
|
async function copyCurrentUuid() {
|
|
try {
|
|
const uuid = currentUuidDisplay ? currentUuidDisplay.value : modalCurrentUuid?.value;
|
|
if (uuid && navigator.clipboard) {
|
|
await navigator.clipboard.writeText(uuid);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidCopied') : 'UUID copied to clipboard!';
|
|
showNotification(msg, 'success');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error copying UUID:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidCopyFailed') : 'Failed to copy UUID';
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
async function regenerateCurrentUuid() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.resetCurrentUserUuid) {
|
|
const message = window.i18n ? window.i18n.t('confirm.regenerateUuidMessage') : 'Are you sure you want to generate a new UUID? This will change your player identity.';
|
|
const title = window.i18n ? window.i18n.t('confirm.regenerateUuidTitle') : 'Generate New UUID';
|
|
const confirmBtn = window.i18n ? window.i18n.t('confirm.regenerateUuidButton') : 'Generate';
|
|
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
|
|
|
|
showCustomConfirm(
|
|
message,
|
|
title,
|
|
async () => {
|
|
await performRegenerateUuid();
|
|
},
|
|
null,
|
|
confirmBtn,
|
|
cancelBtn
|
|
);
|
|
} else {
|
|
console.error('electronAPI.resetCurrentUserUuid not available');
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidRegenNotAvailable') : 'UUID regeneration not available';
|
|
showNotification(msg, 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error in regenerateCurrentUuid:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidRegenFailed') : 'Failed to regenerate UUID';
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
async function performRegenerateUuid() {
|
|
try {
|
|
const result = await window.electronAPI.resetCurrentUserUuid();
|
|
if (result.success && result.uuid) {
|
|
if (currentUuidDisplay) currentUuidDisplay.value = result.uuid;
|
|
if (modalCurrentUuid) modalCurrentUuid.value = result.uuid;
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidGenerated') : 'New UUID generated successfully!';
|
|
showNotification(msg, 'success');
|
|
|
|
if (uuidModal && uuidModal.style.display !== 'none') {
|
|
await loadAllUuids();
|
|
}
|
|
} else {
|
|
throw new Error(result.error || 'Failed to generate new UUID');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error regenerating UUID:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidRegenFailed').replace('{error}', error.message) : `Failed to regenerate UUID: ${error.message}`;
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
async function openUuidModal() {
|
|
try {
|
|
if (uuidModal) {
|
|
uuidModal.style.display = 'flex';
|
|
uuidModal.classList.add('active');
|
|
await loadAllUuids();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error opening UUID modal:', error);
|
|
}
|
|
}
|
|
|
|
function closeUuidModal() {
|
|
if (uuidModal) {
|
|
uuidModal.classList.remove('active');
|
|
setTimeout(() => {
|
|
uuidModal.style.display = 'none';
|
|
}, 300);
|
|
}
|
|
}
|
|
|
|
async function loadAllUuids() {
|
|
try {
|
|
if (!uuidList) return;
|
|
|
|
uuidList.innerHTML = `
|
|
<div class="uuid-loading">
|
|
<i class="fas fa-spinner fa-spin"></i>
|
|
Loading UUIDs...
|
|
</div>
|
|
`;
|
|
|
|
if (window.electronAPI && window.electronAPI.getAllUuidMappings) {
|
|
const mappings = await window.electronAPI.getAllUuidMappings();
|
|
|
|
if (mappings.length === 0) {
|
|
uuidList.innerHTML = `
|
|
<div class="uuid-loading">
|
|
<i class="fas fa-info-circle"></i>
|
|
No UUIDs found
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
uuidList.innerHTML = '';
|
|
|
|
for (const mapping of mappings) {
|
|
const item = document.createElement('div');
|
|
item.className = `uuid-list-item${mapping.isCurrent ? ' current' : ''}`;
|
|
|
|
item.innerHTML = `
|
|
<div class="uuid-item-info">
|
|
<div class="uuid-item-username">${escapeHtml(mapping.username)}</div>
|
|
<div class="uuid-item-uuid">${mapping.uuid}</div>
|
|
</div>
|
|
<div class="uuid-item-actions">
|
|
${mapping.isCurrent ? '<div class="uuid-item-current-badge">Current</div>' : ''}
|
|
<button class="uuid-item-btn copy" onclick="copyUuid('${mapping.uuid}')" title="Copy UUID">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
${!mapping.isCurrent ? `<button class="uuid-item-btn delete" onclick="deleteUuid('${escapeHtml(mapping.username)}')" title="Delete UUID">
|
|
<i class="fas fa-trash"></i>
|
|
</button>` : ''}
|
|
</div>
|
|
`;
|
|
|
|
uuidList.appendChild(item);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading UUIDs:', error);
|
|
if (uuidList) {
|
|
uuidList.innerHTML = `
|
|
<div class="uuid-loading">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
Error loading UUIDs
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
}
|
|
|
|
async function generateNewUuid() {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.generateNewUuid) {
|
|
const newUuid = await window.electronAPI.generateNewUuid();
|
|
if (newUuid) {
|
|
if (customUuidInput) customUuidInput.value = newUuid;
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidGeneratedShort') : 'New UUID generated!';
|
|
showNotification(msg, 'success');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error generating new UUID:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidGenerateFailed') : 'Failed to generate new UUID';
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
async function setCustomUuid() {
|
|
try {
|
|
if (!customUuidInput || !customUuidInput.value.trim()) {
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidRequired') : 'Please enter a UUID';
|
|
showNotification(msg, 'error');
|
|
return;
|
|
}
|
|
|
|
const uuid = customUuidInput.value.trim();
|
|
|
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
if (!uuidRegex.test(uuid)) {
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidInvalidFormat') : 'Invalid UUID format';
|
|
showNotification(msg, 'error');
|
|
return;
|
|
}
|
|
|
|
const message = window.i18n ? window.i18n.t('confirm.setCustomUuidMessage') : 'Are you sure you want to set this custom UUID? This will change your player identity.';
|
|
const title = window.i18n ? window.i18n.t('confirm.setCustomUuidTitle') : 'Set Custom UUID';
|
|
const confirmBtn = window.i18n ? window.i18n.t('confirm.setCustomUuidButton') : 'Set UUID';
|
|
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
|
|
|
|
showCustomConfirm(
|
|
message,
|
|
title,
|
|
async () => {
|
|
await performSetCustomUuid(uuid);
|
|
},
|
|
null,
|
|
confirmBtn,
|
|
cancelBtn
|
|
);
|
|
} catch (error) {
|
|
console.error('Error in setCustomUuid:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidSetFailed') : 'Failed to set custom UUID';
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
async function performSetCustomUuid(uuid) {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.setUuidForUser) {
|
|
const username = getCurrentPlayerName();
|
|
const result = await window.electronAPI.setUuidForUser(username, uuid);
|
|
|
|
if (result.success) {
|
|
if (currentUuidDisplay) currentUuidDisplay.value = uuid;
|
|
if (modalCurrentUuid) modalCurrentUuid.value = uuid;
|
|
if (customUuidInput) customUuidInput.value = '';
|
|
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidSetSuccess') : 'Custom UUID set successfully!';
|
|
showNotification(msg, 'success');
|
|
|
|
await loadAllUuids();
|
|
} else {
|
|
throw new Error(result.error || 'Failed to set custom UUID');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error setting custom UUID:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidSetFailed').replace('{error}', error.message) : `Failed to set custom UUID: ${error.message}`;
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
window.copyUuid = async function(uuid) {
|
|
try {
|
|
if (navigator.clipboard) {
|
|
await navigator.clipboard.writeText(uuid);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidCopied') : 'UUID copied to clipboard!';
|
|
showNotification(msg, 'success');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error copying UUID:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidCopyFailed') : 'Failed to copy UUID';
|
|
showNotification(msg, 'error');
|
|
}
|
|
};
|
|
|
|
window.deleteUuid = async function(username) {
|
|
try {
|
|
const message = window.i18n ? window.i18n.t('confirm.deleteUuidMessage').replace('{username}', username) : `Are you sure you want to delete the UUID for "${username}"? This action cannot be undone.`;
|
|
const title = window.i18n ? window.i18n.t('confirm.deleteUuidTitle') : 'Delete UUID';
|
|
const confirmBtn = window.i18n ? window.i18n.t('confirm.deleteUuidButton') : 'Delete';
|
|
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
|
|
|
|
showCustomConfirm(
|
|
message,
|
|
title,
|
|
async () => {
|
|
await performDeleteUuid(username);
|
|
},
|
|
null,
|
|
confirmBtn,
|
|
cancelBtn
|
|
);
|
|
} catch (error) {
|
|
console.error('Error in deleteUuid:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteFailed') : 'Failed to delete UUID';
|
|
showNotification(msg, 'error');
|
|
}
|
|
};
|
|
|
|
async function performDeleteUuid(username) {
|
|
try {
|
|
if (window.electronAPI && window.electronAPI.deleteUuidForUser) {
|
|
const result = await window.electronAPI.deleteUuidForUser(username);
|
|
|
|
if (result.success) {
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteSuccess') : 'UUID deleted successfully!';
|
|
showNotification(msg, 'success');
|
|
await loadAllUuids();
|
|
} else {
|
|
throw new Error(result.error || 'Failed to delete UUID');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting UUID:', error);
|
|
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteFailed').replace('{error}', error.message) : `Failed to delete UUID: ${error.message}`;
|
|
showNotification(msg, 'error');
|
|
}
|
|
}
|
|
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function showNotification(message, type = 'info') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification notification-${type}`;
|
|
notification.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 1rem 1.5rem;
|
|
border-radius: 8px;
|
|
color: white;
|
|
font-weight: 600;
|
|
z-index: 10000;
|
|
opacity: 0;
|
|
transform: translateX(100%);
|
|
transition: all 0.3s ease;
|
|
`;
|
|
|
|
if (type === 'success') {
|
|
notification.style.background = 'linear-gradient(135deg, #22c55e, #16a34a)';
|
|
} else if (type === 'error') {
|
|
notification.style.background = 'linear-gradient(135deg, #ef4444, #dc2626)';
|
|
} else {
|
|
notification.style.background = 'linear-gradient(135deg, #3b82f6, #2563eb)';
|
|
}
|
|
|
|
notification.innerHTML = `
|
|
<i class="fas fa-${type === 'success' ? 'check' : type === 'error' ? 'exclamation-triangle' : 'info-circle'}"></i>
|
|
${message}
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
setTimeout(() => {
|
|
notification.style.opacity = '1';
|
|
notification.style.transform = 'translateX(0)';
|
|
}, 100);
|
|
|
|
setTimeout(() => {
|
|
notification.style.opacity = '0';
|
|
notification.style.transform = 'translateX(100%)';
|
|
setTimeout(() => {
|
|
if (notification.parentNode) {
|
|
notification.parentNode.removeChild(notification);
|
|
}
|
|
}, 300);
|
|
}, 3000);
|
|
} |