update 2.0.2

This commit is contained in:
AMIAY
2026-01-19 23:15:29 +01:00
parent 300616ba82
commit 21f8527ed4
24 changed files with 3376 additions and 346 deletions

View File

@@ -205,9 +205,15 @@
<i class="fas fa-comments mr-2"></i>
PLAYERS CHAT
</h2>
<div class="chat-online-badge">
<i class="fas fa-circle"></i>
<span id="chatOnlineCount">0</span> online
<div class="chat-header-actions">
<button id="chatColorBtn" class="chat-color-btn">
<i class="fas fa-palette"></i>
<span>Color</span>
</button>
<div class="chat-online-badge">
<i class="fas fa-circle"></i>
<span id="chatOnlineCount">0</span> online
</div>
</div>
</div>
@@ -291,6 +297,24 @@
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fab fa-discord"></i>
Discord Integration
</h3>
<div class="settings-option">
<label class="settings-checkbox">
<input type="checkbox" id="discordRPCCheck" checked />
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title">Enable Discord Rich Presence</div>
<div class="checkbox-description">Show your launcher activity on Discord</div>
</div>
</label>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-gamepad"></i>
@@ -326,6 +350,50 @@
</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-fingerprint"></i>
Player UUID Management
</h3>
<div class="settings-option">
<div class="settings-input-group">
<label class="settings-input-label">Current UUID</label>
<div class="uuid-display-container">
<input
type="text"
id="currentUuid"
class="settings-input uuid-input"
readonly
placeholder="Loading UUID..."
/>
<button id="copyUuidBtn" class="uuid-btn copy-btn" title="Copy UUID">
<i class="fas fa-copy"></i>
</button>
<button id="regenerateUuidBtn" class="uuid-btn regenerate-btn" title="Generate New UUID">
<i class="fas fa-sync-alt"></i>
</button>
</div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
Your unique player identifier for this username
</p>
</div>
</div>
<div class="settings-option">
<div class="settings-button-group">
<button id="manageUuidsBtn" class="settings-action-btn">
<i class="fas fa-list"></i>
<div class="btn-content">
<div class="btn-title">Manage All UUIDs</div>
<div class="btn-description">View and manage all player UUIDs</div>
</div>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -415,16 +483,84 @@
</div>
</div>
<!-- UUID Management Modal -->
<div id="uuidModal" class="uuid-modal" style="display: none;">
<div class="uuid-modal-content">
<div class="uuid-modal-header">
<h2 class="uuid-modal-title">
<i class="fas fa-fingerprint mr-2"></i>
UUID Management
</h2>
<button id="uuidModalClose" class="modal-close-btn">
<i class="fas fa-times"></i>
</button>
</div>
<div class="uuid-modal-body">
<div class="uuid-current-section">
<h3 class="uuid-section-title">Current User UUID</h3>
<div class="uuid-current-display">
<input type="text" id="modalCurrentUuid" class="uuid-display-input" readonly />
<button id="modalCopyUuidBtn" class="uuid-action-btn copy-btn" title="Copy UUID">
<i class="fas fa-copy"></i>
</button>
<button id="modalRegenerateUuidBtn" class="uuid-action-btn regenerate-btn" title="Generate New UUID">
<i class="fas fa-sync-alt"></i>
</button>
</div>
</div>
<div class="uuid-list-section">
<div class="uuid-list-header">
<h3 class="uuid-section-title">All Player UUIDs</h3>
<button id="generateNewUuidBtn" class="uuid-generate-btn">
<i class="fas fa-plus"></i>
Generate New UUID
</button>
</div>
<div id="uuidList" class="uuid-list">
<div class="uuid-loading">
<i class="fas fa-spinner fa-spin"></i>
Loading UUIDs...
</div>
</div>
</div>
<div class="uuid-custom-section">
<h3 class="uuid-section-title">Set Custom UUID</h3>
<div class="uuid-custom-form">
<input
type="text"
id="customUuidInput"
class="uuid-input"
placeholder="Enter custom UUID (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)"
maxlength="36"
/>
<button id="setCustomUuidBtn" class="uuid-set-btn">
<i class="fas fa-check"></i>
Set UUID
</button>
</div>
<p class="uuid-custom-hint">
<i class="fas fa-exclamation-triangle"></i>
Warning: Setting a custom UUID will change your current player identity
</p>
</div>
</div>
</div>
</div>
<footer class="fixed bottom-0 left-0 right-0 z-50 bg-black/80 backdrop-blur-sm px-4 py-2">
<div class="flex items-center justify-center text-xs text-gray-400">
<span>Made by <a href="https://github.com/amiayweb" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@amiayweb</a></span>
<span>Made by <a href="https://github.com/amiayweb" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@amiayweb</a> & <a href="https://github.com/Relyz1993" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@Relyz</a></span>
<span class="mx-2">|</span>
<span>Contributors:
<a href="https://github.com/chasem-dev" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@chasem-dev</a>,
<a href="https://github.com/crimera" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@crimera</a>,
<a href="https://github.com/sanasol" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@sanasol</a>,
<a href="https://github.com/Terromur" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@terromur</a>,
<a href="https://github.com/ericiskoolbeans" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@ericiskoolbeans</a>
<a href="https://github.com/ericiskoolbeans" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@ericiskoolbeans</a>,
<a href="https://github.com/fazrigading" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">@fazrigading</a>
</span>
</div>
</footer>
@@ -442,6 +578,50 @@
<i class="fas fa-times"></i>
</button>
</div>
<!-- Modal pour sélectionner la couleur du chat -->
<div id="chatColorModal" class="chat-color-modal" style="display: none;">
<div class="chat-color-modal-content">
<div class="chat-color-modal-header">
<h3 class="chat-color-modal-title">
<i class="fas fa-palette"></i>
Customize Username Color
</h3>
<button class="modal-close-btn" onclick="closeChatColorModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="chat-color-modal-body">
<div id="solidColorSection" class="color-section">
<h4>Choose a solid color:</h4>
<div class="predefined-colors">
<div class="color-option" data-color="#3498db" style="background: #3498db;"></div>
<div class="color-option" data-color="#e74c3c" style="background: #e74c3c;"></div>
<div class="color-option" data-color="#2ecc71" style="background: #2ecc71;"></div>
<div class="color-option" data-color="#f39c12" style="background: #f39c12;"></div>
<div class="color-option" data-color="#9b59b6" style="background: #9b59b6;"></div>
<div class="color-option" data-color="#1abc9c" style="background: #1abc9c;"></div>
<div class="color-option" data-color="#e91e63" style="background: #e91e63;"></div>
<div class="color-option" data-color="#ff5722" style="background: #ff5722;"></div>
</div>
<div class="custom-color-input">
<label for="customColor">Custom color:</label>
<input type="color" id="customColor" value="#3498db">
</div>
</div>
<div class="color-preview">
<h4>Preview:</h4>
<div id="colorPreview" class="preview-username">YourUsername</div>
</div>
</div>
<div class="chat-color-modal-footer">
<button class="btn-secondary" onclick="closeChatColorModal()">Cancel</button>
<button class="btn-primary" onclick="applyChatColor()">Apply Color</button>
</div>
</div>
</div>
<script type="module" src="js/update.js"></script>
</body>

View File

@@ -3,13 +3,26 @@ let socket = null;
let isAuthenticated = false;
let messageQueue = [];
let chatUsername = '';
const SOCKET_URL = 'http://3.10.208.30:3001';
let userColor = '#3498db';
let userBadge = null;
const SOCKET_URL = 'https://chat.hytalef2p.com';
const MAX_MESSAGE_LENGTH = 500;
async function getOrCreatePlayerId() {
return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
export async function initChat() {
if (window.electronAPI?.loadChatUsername) {
chatUsername = await window.electronAPI.loadChatUsername();
}
if (window.electronAPI?.loadChatColor) {
const savedColor = await window.electronAPI.loadChatColor();
if (savedColor) {
userColor = savedColor;
}
}
if (!chatUsername || chatUsername.trim() === '') {
showUsernameModal();
@@ -17,6 +30,7 @@ export async function initChat() {
}
setupChatUI();
setupColorSelector();
await connectToChat();
}
@@ -136,13 +150,22 @@ async function connectToChat() {
reconnectionDelay: 1000
});
socket.on('connect', () => {
socket.on('connect', async () => {
console.log('Connected to chat server');
socket.emit('authenticate', { username: chatUsername, userId });
const uuid = await window.electronAPI?.getCurrentUuid();
socket.emit('authenticate', {
username: chatUsername,
userId,
uuid: uuid,
userColor: userColor
});
});
socket.on('authenticated', (data) => {
isAuthenticated = true;
userBadge = data.badge;
addSystemMessage(`Connected as ${data.username}`);
while (messageQueue.length > 0) {
@@ -155,7 +178,7 @@ async function connectToChat() {
if (data.type === 'system') {
addSystemMessage(data.message);
} else if (data.type === 'user') {
addUserMessage(data.username, data.message, data.timestamp);
addUserMessage(data.username, data.message, data.timestamp, data.userColor, data.badge);
}
});
@@ -226,7 +249,7 @@ function sendMessage() {
updateCharCounter();
}
function addUserMessage(username, message, timestamp) {
function addUserMessage(username, message, timestamp, userColor = '#3498db', badge = null) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) return;
@@ -238,14 +261,35 @@ function addUserMessage(username, message, timestamp) {
minute: '2-digit'
});
let badgeHTML = '';
if (badge) {
let badgeStyle = '';
if (badge.style === 'rainbow') {
badgeStyle = `background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4, #ffeaa7, #fab1a0, #fd79a8); background-size: 400% 400%; animation: rainbow 3s ease infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-weight: bold; display: inline;`;
} else if (badge.style === 'gradient') {
if (badge.badge === 'CONTRIBUTOR') {
badgeStyle = `background: linear-gradient(45deg, #22c55e, #16a34a); background-size: 200% 200%; animation: contributorGlow 2s ease infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-weight: bold; display: inline;`;
} else {
badgeStyle = `color: ${badge.color}; font-weight: bold; display: inline;`;
}
}
badgeHTML = `<span class="user-badge" style="${badgeStyle}">[${badge.badge}]</span> `;
}
messageDiv.innerHTML = `
<div class="message-header">
<span class="message-username">${escapeHtml(username)}</span>
<span class="message-user-info">${badgeHTML}<span class="message-username" style="font-weight: bold;" data-username-color="${userColor}">${escapeHtml(username)}</span></span>
<span class="message-time">${time}</span>
</div>
<div class="message-content">${message}</div>
`;
const usernameElement = messageDiv.querySelector('.message-username');
if (usernameElement) {
applyUserColorStyle(usernameElement, userColor);
}
chatMessages.appendChild(messageDiv);
scrollToBottom();
}
@@ -352,6 +396,104 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
function setupColorSelector() {
const colorBtn = document.getElementById('chatColorBtn');
if (colorBtn) {
colorBtn.addEventListener('click', showChatColorModal);
}
const colorOptions = document.querySelectorAll('.color-option');
colorOptions.forEach(option => {
option.addEventListener('click', () => {
document.querySelectorAll('.color-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
updateColorPreview();
});
});
const customColor = document.getElementById('customColor');
if (customColor) {
customColor.addEventListener('input', () => {
document.querySelectorAll('.color-option').forEach(o => o.classList.remove('selected'));
updateColorPreview();
});
}
}
function showChatColorModal() {
const modal = document.getElementById('chatColorModal');
if (modal) {
modal.style.display = 'flex';
updateColorPreview();
}
}
window.closeChatColorModal = function() {
const modal = document.getElementById('chatColorModal');
if (modal) {
modal.style.display = 'none';
}
}
function updateColorPreview() {
const preview = document.getElementById('colorPreview');
if (!preview) return;
const selectedOption = document.querySelector('.color-option.selected');
let color = '#3498db';
if (selectedOption) {
color = selectedOption.dataset.color;
} else {
const customColor = document.getElementById('customColor');
if (customColor) color = customColor.value;
}
preview.style.color = color;
preview.style.background = 'transparent';
preview.style.webkitBackgroundClip = 'initial';
preview.style.webkitTextFillColor = 'initial';
}
window.applyChatColor = async function() {
let newColor;
const selectedOption = document.querySelector('.color-option.selected');
if (selectedOption) {
newColor = selectedOption.dataset.color;
} else {
const customColor = document.getElementById('customColor');
newColor = customColor ? customColor.value : '#3498db';
}
userColor = newColor;
if (window.electronAPI?.saveChatColor) {
await window.electronAPI.saveChatColor(newColor);
}
if (socket && isAuthenticated) {
const uuid = await window.electronAPI?.getCurrentUuid();
socket.emit('authenticate', {
username: chatUsername,
userId: await getOrCreatePlayerId(),
uuid: uuid,
userColor: userColor
});
addSystemMessage('Username color updated successfully', 'success');
}
closeChatColorModal();
}
function applyUserColorStyle(element, color) {
element.style.color = color;
element.style.background = 'transparent';
element.style.webkitBackgroundClip = 'initial';
element.style.webkitTextFillColor = 'initial';
}
window.ChatAPI = {
send: sendMessage,
disconnect: () => socket?.disconnect()

View File

@@ -33,21 +33,12 @@ export function setupInstallation() {
if (window.electronAPI && window.electronAPI.onProgressUpdate) {
window.electronAPI.onProgressUpdate((data) => {
if (!isDownloading) return;
if (window.LauncherUI) {
window.LauncherUI.showProgress();
window.LauncherUI.updateProgress(data);
}
});
}
if (window.electronAPI && window.electronAPI.onProgressComplete) {
window.electronAPI.onProgressComplete(() => {
if (window.LauncherUI) {
window.LauncherUI.hideProgress();
}
resetInstallButton();
});
}
}
export async function installGame() {
@@ -75,6 +66,7 @@ export async function installGame() {
window.LauncherUI.showLauncherOrInstall(true);
const playerNameInput = document.getElementById('playerName');
if (playerNameInput) playerNameInput.value = playerName;
resetInstallButton();
}, 2000);
}
} else {
@@ -210,3 +202,9 @@ document.addEventListener('DOMContentLoaded', async () => {
setupInstallation();
await checkGameStatusAndShowInterface();
});
window.browseInstallPath = browseInstallPath;
document.addEventListener('DOMContentLoaded', async () => {
setupInstallation();
await checkGameStatusAndShowInterface();
});

View File

@@ -25,21 +25,12 @@ export function setupLauncher() {
if (window.electronAPI && window.electronAPI.onProgressUpdate) {
window.electronAPI.onProgressUpdate((data) => {
if (!isDownloading) return;
if (window.LauncherUI) {
window.LauncherUI.showProgress();
window.LauncherUI.updateProgress(data);
}
});
}
if (window.electronAPI && window.electronAPI.onProgressComplete) {
window.electronAPI.onProgressComplete(() => {
if (window.LauncherUI) {
window.LauncherUI.hideProgress();
}
resetPlayButton();
});
}
}
export async function launch() {
@@ -65,48 +56,181 @@ export async function launch() {
}
try {
if (window.LauncherUI) window.LauncherUI.updateProgress({ message: 'Starting game...' });
if (window.electronAPI && window.electronAPI.launchGame) {
const result = await window.electronAPI.launchGame(playerName, javaPath, '');
isDownloading = false;
if (window.LauncherUI) {
window.LauncherUI.hideProgress();
}
resetPlayButton();
if (result.success) {
if (window.LauncherUI) {
window.LauncherUI.updateProgress({ message: 'Game started successfully!' });
if (window.electronAPI.minimizeWindow) {
setTimeout(() => {
window.LauncherUI.hideProgress();
if (window.electronAPI.minimizeWindow) {
window.electronAPI.minimizeWindow();
}
}, 2000);
window.electronAPI.minimizeWindow();
}, 500);
}
} else {
throw new Error(result.error || 'Launch failed');
console.error('Launch failed:', result.error);
}
} else {
setTimeout(() => {
if (window.LauncherUI) {
window.LauncherUI.updateProgress({ message: 'Game started successfully!' });
setTimeout(() => {
window.LauncherUI.hideProgress();
resetPlayButton();
}, 2000);
}
}, 2000);
isDownloading = false;
if (window.LauncherUI) {
window.LauncherUI.hideProgress();
}
resetPlayButton();
}
} catch (error) {
isDownloading = false;
if (window.LauncherUI) {
window.LauncherUI.updateProgress({ message: `Failed: ${error.message}` });
setTimeout(() => {
window.LauncherUI.hideProgress();
resetPlayButton();
}, 3000);
window.LauncherUI.hideProgress();
}
resetPlayButton();
console.error('Launch error:', error);
}
}
export async function uninstallGame() {
if (!confirm('Are you sure you want to uninstall Hytale? All game files will be deleted.')) {
return;
function showCustomConfirm(message, title = 'Confirm Action', onConfirm, onCancel = null, confirmText = 'Confirm', cancelText = '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(239, 68, 68, 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: #ef4444;">
<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: #ef4444;
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 async function uninstallGame() {
showCustomConfirm(
'Are you sure you want to uninstall Hytale? All game files will be deleted.',
'Uninstall Game',
async () => {
await performUninstall();
},
null,
'Uninstall',
'Cancel'
);
}
async function performUninstall() {
if (window.LauncherUI) window.LauncherUI.showProgress();
if (window.LauncherUI) window.LauncherUI.updateProgress({ message: 'Uninstalling game...' });

View File

@@ -522,7 +522,6 @@ function showNotification(message, type = 'info', duration = 4000) {
}, duration);
}
// Custom confirmation modal
function showConfirmModal(message, onConfirm, onCancel = null) {
const existingModal = document.querySelector('.mod-confirm-modal');
if (existingModal) {

View File

@@ -1,5 +1,5 @@
const API_URL = 'http://3.10.208.30/api';
const API_URL = 'https://api.hytalef2p.com/api';
let updateInterval = null;
let currentUserId = null;

View File

@@ -7,7 +7,6 @@ import './players.js';
import './chat.js';
import './settings.js';
// Discord notification functions
window.closeDiscordNotification = function() {
const notification = document.getElementById('discordNotification');
if (notification) {
@@ -18,23 +17,20 @@ window.closeDiscordNotification = function() {
}
};
// Show notification after a delay
document.addEventListener('DOMContentLoaded', () => {
const notification = document.getElementById('discordNotification');
if (notification) {
// Check if user has previously dismissed the notification
const dismissed = localStorage.getItem('discordNotificationDismissed');
if (!dismissed) {
setTimeout(() => {
notification.style.display = 'flex';
}, 3000); // Show after 3 seconds
}, 3000);
} else {
notification.style.display = 'none';
}
}
});
// Remember when user closes notification
const originalClose = window.closeDiscordNotification;
window.closeDiscordNotification = function() {
localStorage.setItem('discordNotificationDismissed', 'true');

View File

@@ -4,6 +4,143 @@ let customJavaOptions;
let customJavaPath;
let browseJavaBtn;
let settingsPlayerName;
let discordRPCCheck;
// 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 = 'Confirm Action', onConfirm, onCancel = null, confirmText = 'Confirm', cancelText = '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();
@@ -16,6 +153,22 @@ function setupSettingsElements() {
customJavaPath = document.getElementById('customJavaPath');
browseJavaBtn = document.getElementById('browseJavaBtn');
settingsPlayerName = document.getElementById('settingsPlayerName');
discordRPCCheck = document.getElementById('discordRPCCheck');
// 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);
@@ -28,6 +181,51 @@ function setupSettingsElements() {
if (settingsPlayerName) {
settingsPlayerName.addEventListener('change', savePlayerName);
}
if (discordRPCCheck) {
discordRPCCheck.addEventListener('change', saveDiscordRPC);
}
// 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();
}
});
}
}
function toggleCustomJava() {
@@ -90,24 +288,73 @@ async function loadCustomJavaPath() {
}
}
async function savePlayerName() {
async function saveDiscordRPC() {
try {
if (window.electronAPI && window.electronAPI.saveUsername && settingsPlayerName) {
const playerName = settingsPlayerName.value.trim() || 'Player';
await window.electronAPI.saveUsername(playerName);
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) {
showNotification('Discord Rich Presence enabled', 'success');
} else {
showNotification('Discord Rich Presence disabled', 'success');
}
} else {
throw new Error('Failed to save Discord RPC setting');
}
}
} catch (error) {
console.error('Error saving Discord RPC setting:', error);
showNotification('Failed to save Discord setting', '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 savePlayerName() {
try {
if (!window.electronAPI || !settingsPlayerName) return;
const playerName = settingsPlayerName.value.trim();
if (!playerName) {
showNotification('Please enter a valid player name', 'error');
return;
}
await window.electronAPI.saveUsername(playerName);
showNotification('Player name saved successfully', 'success');
} catch (error) {
console.error('Error saving player name:', error);
showNotification('Failed to save player name', 'error');
}
}
async function loadPlayerName() {
try {
if (window.electronAPI && window.electronAPI.loadUsername && settingsPlayerName) {
const savedName = await window.electronAPI.loadUsername();
if (savedName) {
settingsPlayerName.value = savedName;
}
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);
@@ -117,6 +364,8 @@ async function loadPlayerName() {
async function loadAllSettings() {
await loadCustomJavaPath();
await loadPlayerName();
await loadCurrentUuid();
await loadDiscordRPC();
}
async function openGameLocation() {
@@ -144,7 +393,6 @@ export function getCurrentPlayerName() {
return 'Player';
}
// Make openGameLocation globally available
window.openGameLocation = openGameLocation;
document.addEventListener('DOMContentLoaded', initSettings);
@@ -152,4 +400,329 @@ 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);
showNotification('UUID copied to clipboard!', 'success');
}
} catch (error) {
console.error('Error copying UUID:', error);
showNotification('Failed to copy UUID', 'error');
}
}
async function regenerateCurrentUuid() {
try {
if (window.electronAPI && window.electronAPI.resetCurrentUserUuid) {
showCustomConfirm(
'Are you sure you want to generate a new UUID? This will change your player identity.',
'Generate New UUID',
async () => {
await performRegenerateUuid();
},
null,
'Generate',
'Cancel'
);
} else {
console.error('electronAPI.resetCurrentUserUuid not available');
showNotification('UUID regeneration not available', 'error');
}
} catch (error) {
console.error('Error in regenerateCurrentUuid:', error);
showNotification('Failed to regenerate UUID', '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;
showNotification('New UUID generated successfully!', '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);
showNotification(`Failed to regenerate UUID: ${error.message}`, '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;
showNotification('New UUID generated!', 'success');
}
}
} catch (error) {
console.error('Error generating new UUID:', error);
showNotification('Failed to generate new UUID', 'error');
}
}
async function setCustomUuid() {
try {
if (!customUuidInput || !customUuidInput.value.trim()) {
showNotification('Please enter a UUID', '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)) {
showNotification('Invalid UUID format', 'error');
return;
}
showCustomConfirm(
'Are you sure you want to set this custom UUID? This will change your player identity.',
'Set Custom UUID',
async () => {
await performSetCustomUuid(uuid);
},
null,
'Set UUID',
'Cancel'
);
} catch (error) {
console.error('Error in setCustomUuid:', error);
showNotification('Failed to set custom UUID', '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 = '';
showNotification('Custom UUID set successfully!', 'success');
await loadAllUuids();
} else {
throw new Error(result.error || 'Failed to set custom UUID');
}
}
} catch (error) {
console.error('Error setting custom UUID:', error);
showNotification(`Failed to set custom UUID: ${error.message}`, 'error');
}
}
window.copyUuid = async function(uuid) {
try {
if (navigator.clipboard) {
await navigator.clipboard.writeText(uuid);
showNotification('UUID copied to clipboard!', 'success');
}
} catch (error) {
console.error('Error copying UUID:', error);
showNotification('Failed to copy UUID', 'error');
}
};
window.deleteUuid = async function(username) {
try {
showCustomConfirm(
`Are you sure you want to delete the UUID for "${username}"? This action cannot be undone.`,
'Delete UUID',
async () => {
await performDeleteUuid(username);
},
null,
'Delete',
'Cancel'
);
} catch (error) {
console.error('Error in deleteUuid:', error);
showNotification('Failed to delete UUID', 'error');
}
};
async function performDeleteUuid(username) {
try {
if (window.electronAPI && window.electronAPI.deleteUuidForUser) {
const result = await window.electronAPI.deleteUuidForUser(username);
if (result.success) {
showNotification('UUID deleted successfully!', 'success');
await loadAllUuids();
} else {
throw new Error(result.error || 'Failed to delete UUID');
}
}
} catch (error) {
console.error('Error deleting UUID:', error);
showNotification(`Failed to delete UUID: ${error.message}`, '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);
}

View File

@@ -3407,6 +3407,12 @@ body {
margin-bottom: 0.5rem;
}
.message-user-info {
display: flex;
align-items: center;
flex-wrap: nowrap;
}
.message-username {
font-weight: 600;
color: #9333ea;
@@ -4330,4 +4336,781 @@ body {
}
}
/* UUID Management Styles */
.uuid-display-container {
display: flex;
align-items: stretch;
gap: 0.5rem;
}
.uuid-input {
flex: 1;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
font-size: 0.875rem;
letter-spacing: 0.5px;
}
.uuid-btn {
padding: 0.875rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
min-width: 44px;
}
.uuid-btn:hover {
background: rgba(147, 51, 234, 0.2);
border-color: rgba(147, 51, 234, 0.4);
color: #9333ea;
transform: translateY(-2px);
}
.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
border-color: rgba(34, 197, 94, 0.4);
color: #22c55e;
}
.regenerate-btn:hover {
background: rgba(249, 115, 22, 0.2);
border-color: rgba(249, 115, 22, 0.4);
color: #f97316;
}
/* UUID Modal Styles */
.uuid-modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
z-index: 9999;
align-items: center;
justify-content: center;
animation: fadeIn 0.3s ease;
}
.uuid-modal.active {
display: flex;
}
.uuid-modal-content {
background: rgba(10, 10, 10, 0.95);
border: 1px solid rgba(147, 51, 234, 0.3);
border-radius: 16px;
width: 90%;
max-width: 800px;
max-height: 80vh;
display: flex;
flex-direction: column;
animation: slideUp 0.3s ease;
box-shadow: 0 20px 60px rgba(147, 51, 234, 0.3);
}
.uuid-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.uuid-modal-title {
font-size: 1.5rem;
font-weight: 700;
font-family: 'Space Grotesk', sans-serif;
color: white;
display: flex;
align-items: center;
margin: 0;
}
.modal-close-btn {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.6);
cursor: pointer;
transition: all 0.3s ease;
}
.modal-close-btn:hover {
background: rgba(255, 0, 0, 0.2);
border-color: rgba(255, 0, 0, 0.3);
color: rgb(239, 68, 68);
}
.uuid-modal-body {
padding: 2rem;
overflow-y: auto;
flex: 1;
display: flex;
flex-direction: column;
gap: 2rem;
}
.uuid-modal-body::-webkit-scrollbar {
width: 8px;
}
.uuid-modal-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.uuid-modal-body::-webkit-scrollbar-thumb {
background: rgba(147, 51, 234, 0.3);
border-radius: 4px;
}
.uuid-modal-body::-webkit-scrollbar-thumb:hover {
background: rgba(147, 51, 234, 0.5);
}
.uuid-section-title {
font-size: 1.25rem;
font-weight: 600;
color: white;
margin: 0 0 1rem 0;
display: flex;
align-items: center;
gap: 0.5rem;
font-family: 'Space Grotesk', sans-serif;
}
.uuid-current-section,
.uuid-list-section,
.uuid-custom-section {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 1.5rem;
}
.uuid-current-display {
display: flex;
align-items: stretch;
gap: 0.75rem;
}
.uuid-display-input {
flex: 1;
padding: 0.875rem 1rem;
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
font-size: 0.875rem;
letter-spacing: 0.5px;
}
.uuid-action-btn {
padding: 0.875rem 1rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
min-width: 44px;
}
.uuid-action-btn:hover {
background: rgba(147, 51, 234, 0.2);
border-color: rgba(147, 51, 234, 0.4);
color: #9333ea;
transform: translateY(-2px);
}
.uuid-list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.uuid-generate-btn {
padding: 0.75rem 1.25rem;
background: linear-gradient(135deg, #9333ea, #3b82f6);
border: none;
border-radius: 8px;
color: white;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.uuid-generate-btn:hover {
background: linear-gradient(135deg, #a855f7, #60a5fa);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(147, 51, 234, 0.3);
}
.uuid-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-height: 300px;
overflow-y: auto;
}
.uuid-list::-webkit-scrollbar {
width: 6px;
}
.uuid-list::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.uuid-list::-webkit-scrollbar-thumb {
background: rgba(147, 51, 234, 0.3);
border-radius: 3px;
}
.uuid-list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
transition: all 0.3s ease;
}
.uuid-list-item:hover {
border-color: rgba(147, 51, 234, 0.3);
background: rgba(147, 51, 234, 0.05);
}
.uuid-list-item.current {
border-color: rgba(34, 197, 94, 0.4);
background: rgba(34, 197, 94, 0.1);
}
.uuid-item-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.uuid-item-username {
font-size: 0.95rem;
font-weight: 600;
color: white;
}
.uuid-item-uuid {
font-size: 0.8rem;
font-family: 'JetBrains Mono', monospace;
color: rgba(255, 255, 255, 0.6);
letter-spacing: 0.5px;
}
.uuid-item-actions {
display: flex;
gap: 0.5rem;
align-items: center;
}
.uuid-item-current-badge {
padding: 0.25rem 0.75rem;
background: rgba(34, 197, 94, 0.2);
border: 1px solid rgba(34, 197, 94, 0.3);
border-radius: 12px;
color: #22c55e;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uuid-custom-form {
display: flex;
align-items: stretch;
gap: 0.75rem;
}
.uuid-set-btn {
padding: 0.875rem 1.5rem;
background: linear-gradient(135deg, #22c55e, #16a34a);
border: none;
border-radius: 8px;
color: white;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
white-space: nowrap;
}
.uuid-set-btn:hover {
background: linear-gradient(135deg, #16a34a, #15803d);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(34, 197, 94, 0.3);
}
.uuid-custom-hint {
margin-top: 1rem;
font-size: 0.85rem;
color: rgba(249, 115, 22, 0.9);
display: flex;
align-items: center;
gap: 0.5rem;
}
.uuid-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 2rem;
color: rgba(255, 255, 255, 0.6);
font-size: 0.95rem;
}
.uuid-loading i {
font-size: 1.25rem;
color: #9333ea;
}
.uuid-item-btn {
padding: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
color: rgba(255, 255, 255, 0.6);
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.875rem;
display: flex;
align-items: center;
justify-content: center;
min-width: 32px;
height: 32px;
}
.uuid-item-btn:hover {
background: rgba(147, 51, 234, 0.2);
border-color: rgba(147, 51, 234, 0.4);
color: #9333ea;
}
.uuid-item-btn.copy:hover {
background: rgba(34, 197, 94, 0.2);
border-color: rgba(34, 197, 94, 0.4);
color: #22c55e;
}
.uuid-item-btn.delete:hover {
background: rgba(239, 68, 68, 0.2);
border-color: rgba(239, 68, 68, 0.4);
color: #ef4444;
}
@media (max-width: 600px) {
.uuid-modal-content {
width: 95vw;
max-height: 90vh;
}
.uuid-modal-body {
padding: 1rem;
gap: 1.5rem;
}
.uuid-current-display,
.uuid-custom-form {
flex-direction: column;
}
.uuid-list-header {
flex-direction: column;
align-items: stretch;
gap: 1rem;
}
.uuid-list-item {
flex-direction: column;
align-items: stretch;
gap: 1rem;
}
.uuid-item-actions {
justify-content: center;
}
}
/* Chat Badges et Animations */
@keyframes rainbow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes contributorGlow {
0%, 100% {
background-position: 0% 50%;
filter: brightness(1);
}
50% {
background-position: 100% 50%;
filter: brightness(1.2);
}
}
.user-badge {
display: inline-block;
font-size: 0.75rem;
font-weight: bold;
margin-right: 0.25rem;
}
.message-username {
font-weight: bold;
}
/* Styles pour le sélecteur de couleur dans le chat */
.chat-header-actions {
display: flex;
align-items: center;
gap: 1rem;
}
.chat-color-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: linear-gradient(135deg, #3b82f6, #06b6d4);
border: 1px solid rgba(59, 130, 246, 0.6);
border-radius: 8px;
color: white;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.875rem;
font-weight: 600;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}
.chat-color-btn:hover {
background: linear-gradient(135deg, #2563eb, #0891b2);
border-color: rgba(37, 99, 235, 0.8);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
/* Modal de sélection de couleur */
.chat-color-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
animation: fadeIn 0.3s ease;
}
.chat-color-modal-content {
background: rgba(20, 20, 20, 0.95);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
animation: slideUp 0.3s ease;
}
.chat-color-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 2rem 2rem 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.chat-color-modal-title {
font-size: 1.5rem;
font-weight: 700;
color: white;
display: flex;
align-items: center;
gap: 0.5rem;
margin: 0;
}
.chat-color-modal-title i {
color: #9333ea;
}
.modal-close-btn {
padding: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.3s ease;
}
.modal-close-btn:hover {
background: rgba(239, 68, 68, 0.2);
border-color: rgba(239, 68, 68, 0.4);
color: #ef4444;
}
.chat-color-modal-body {
padding: 2rem;
}
.color-type-selector {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
.color-type-option {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
font-weight: 500;
color: rgba(255, 255, 255, 0.8);
}
.color-type-option input[type="radio"] {
display: none;
}
.radio-custom {
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
position: relative;
transition: all 0.3s ease;
}
.color-type-option input[type="radio"]:checked + .radio-custom {
border-color: #9333ea;
background: rgba(147, 51, 234, 0.2);
}
.color-type-option input[type="radio"]:checked + .radio-custom::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
background: #9333ea;
border-radius: 50%;
}
.color-section {
margin-bottom: 2rem;
}
.color-section h4 {
font-size: 1.1rem;
font-weight: 600;
color: white;
margin-bottom: 1rem;
}
.predefined-colors {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 0.5rem;
margin-bottom: 1rem;
}
.color-option {
width: 40px;
height: 40px;
border-radius: 8px;
cursor: pointer;
border: 2px solid rgba(255, 255, 255, 0.2);
transition: all 0.3s ease;
}
.color-option:hover,
.color-option.selected {
transform: scale(1.1);
border-color: white;
box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
}
.custom-color-input,
.gradient-color-input {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.custom-color-input label,
.gradient-color-input label {
font-weight: 500;
color: rgba(255, 255, 255, 0.8);
min-width: 80px;
}
.custom-color-input input[type="color"],
.gradient-color-input input[type="color"] {
width: 60px;
height: 40px;
border: none;
border-radius: 8px;
cursor: pointer;
background: transparent;
}
.gradient-controls {
display: flex;
flex-direction: column;
gap: 1rem;
}
.gradient-direction {
display: flex;
align-items: center;
gap: 1rem;
}
.gradient-direction label {
font-weight: 500;
color: rgba(255, 255, 255, 0.8);
min-width: 80px;
}
.gradient-direction select {
padding: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: white;
cursor: pointer;
}
.color-preview {
margin-top: 2rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.preview-username {
font-size: 1.2rem;
font-weight: bold;
text-align: center;
padding: 1rem;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
}
.chat-color-modal-footer {
padding: 1rem 2rem 2rem;
display: flex;
gap: 1rem;
justify-content: flex-end;
}
.btn-secondary,
.btn-primary {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
}
.btn-primary {
background: linear-gradient(135deg, #9333ea, #3b82f6);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(147, 51, 234, 0.4);
}
.color-picker-container {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
}
.color-option {
width: 30px;
height: 30px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: all 0.2s ease;
}
.color-option:hover {
transform: scale(1.1);
}
.color-option.selected {
border-color: #fff;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}