<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>k/place</title>
    <!-- Google Fonts for Modern Typography -->
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
    <style>
        /* CSS Variables for Easy Theming */
        :root {
            --primary-color: #2c3e50;
            --secondary-color: #34495e;
            --accent-color: #e74c3c;
            --background-color: #ecf0f1;
            --drawer-width: 300px;
            --transition-speed: 0.3s;
            --button-hover: #2980b9;
            --input-background: #ffffff;
            --input-border: #bdc3c7;
            --text-color: #2c3e50;
            --header-background: #2c3e50;
            --drawer-background: #34495e;
            --drawer-tab-color: #e74c3c;
            --minimap-border: #2c3e50;
            --tooltip-background: rgba(0, 0, 0, 0.8);
            --tooltip-color: #ffffff;
        }

        /* Reset and Base Styles */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Roboto', sans-serif;
            background-color: var(--background-color);
            color: var(--text-color);
            display: flex;
            flex-direction: column;
            height: 100vh;
            overflow: hidden;
        }

        /* Header Styles */
        header {
            background-color: var(--header-background);
            color: white;
            padding: 15px 20px;
            text-align: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            z-index: 100;
            position: relative;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        header h1 {
            font-weight: 700;
            margin-bottom: 5px;
            cursor: pointer;
        }

        header .user-info {
            display: flex;
            align-items: center;
            gap: 15px;
        }

        header .user-info img {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            object-fit: cover;
            border: 2px solid #fff;
        }

        /* Main Layout */
        main {
            display: flex;
            flex: 1;
            position: relative;
        }

        /* Left Drawer Styles */
        #drawer {
            position: fixed;
            top: 60px; /* Adjusted for header height */
            left: 0;
            width: var(--drawer-width);
            height: calc(100% - 60px);
            background-color: var(--drawer-background);
            color: white;
            padding: 20px;
            transform: translateX(-280px);
            transition: transform var(--transition-speed) ease;
            z-index: 1000;
            box-shadow: 2px 0 5px rgba(0,0,0,0.1);
            overflow-y: auto;
        }

        #drawer:hover {
            transform: translateX(0);
        }

        /* Drawer Tab */
        #drawer::before {
            content: '☰';
            position: absolute;
            right: -40px;
            top: 50%;
            transform: translateY(-50%);
            background-color: var(--drawer-tab-color);
            color: white;
            width: 40px;
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            border-top-right-radius: 8px;
            border-bottom-right-radius: 8px;
            font-size: 1.5rem;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
            transition: background-color var(--transition-speed) ease;
        }

        #drawer:hover::before {
            background-color: #c0392b; /* Slightly darker shade */
        }

        /* Drawer Content Sections */
        #drawer h3 {
            margin-bottom: 15px;
            font-weight: 500;
            border-bottom: 1px solid #bdc3c7;
            padding-bottom: 5px;
        }

        section {
            margin-bottom: 20px;
        }

        /* Color Palette */
        .color-palette {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-bottom: 20px;
        }

        .color-btn {
            width: 30px;
            height: 30px;
            border: 2px solid transparent;
            border-radius: 4px;
            cursor: pointer;
            transition: border 0.2s ease, transform 0.2s ease;
        }

        .color-btn.selected {
            border: 2px solid white;
            transform: scale(1.1);
        }

        /* Color Picker */
        .color-picker {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 20px;
        }

        .color-picker input[type="color"] {
            width: 40px;
            height: 40px;
            border: none;
            cursor: pointer;
            padding: 0;
            border-radius: 4px;
        }

        .color-picker label {
            font-size: 0.9rem;
        }

        /* Username Section */
        #usernameSection {
            display: flex;
            flex-direction: column;
            gap: 10px;
            margin-bottom: 20px;
        }

        #usernameSection input {
            padding: 8px 10px;
            border: 1px solid var(--input-border);
            border-radius: 4px;
            font-size: 1rem;
            background-color: var(--input-background);
            color: var(--text-color);
        }

        #usernameSection button {
            padding: 10px;
            background-color: var(--accent-color);
            border: none;
            border-radius: 4px;
            color: white;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.2s ease;
        }

        #usernameSection button:hover {
            background-color: #c0392b; /* Slightly darker shade */
        }

        /* Online Users */
        #onlineUsers {
            font-weight: 500;
            margin-top: 10px;
        }

        /* Chat Section */
        #chatSection {
            /* Styles inherited from section */
        }

        #chatMessages {
            height: 200px; 
            overflow-y: scroll; 
            background-color: #fff; 
            padding: 10px; 
            border-radius: 4px; 
            margin-bottom: 10px;
            color: #000;
        }

        #chatMessages div {
            margin-bottom: 5px;
        }

        #chatInput {
            width: 100%; 
            padding: 8px; 
            border: 1px solid var(--input-border); 
            border-radius: 4px;
        }

        /* Room Section */
        #roomSection {
            /* Styles inherited from section */
        }

        #roomList {
            max-height: 150px; 
            overflow-y: scroll; 
            margin-bottom: 10px;
            background-color: #fff;
            padding: 10px;
            border-radius: 4px;
            color: #000;
        }

        #roomList div {
            padding: 5px;
            cursor: pointer;
            border-bottom: 1px solid #bdc3c7;
        }

        #roomList div:hover {
            background-color: #ecf0f1;
        }

        #newRoomName {
            width: 100%; 
            padding: 8px; 
            border: 1px solid var(--input-border); 
            border-radius: 4px; 
            margin-bottom: 5px;
        }

        #createRoom {
            padding: 10px;
            background-color: var(--accent-color);
            border: none;
            border-radius: 4px;
            color: white;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.2s ease;
            width: 100%;
        }

        #createRoom:hover {
            background-color: #c0392b;
        }

        /* Friends Section */
        #friendsSection {
            /* Styles inherited from section */
        }

        #friendList {
            max-height: 150px; 
            overflow-y: scroll; 
            margin-bottom: 10px;
            background-color: #fff;
            padding: 10px;
            border-radius: 4px;
            color: #000;
        }

        #friendList div {
            padding: 5px;
            cursor: pointer;
            border-bottom: 1px solid #bdc3c7;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        #friendList div:hover {
            background-color: #ecf0f1;
        }

        #friendUsername {
            width: 100%; 
            padding: 8px; 
            border: 1px solid var(--input-border); 
            border-radius: 4px; 
            margin-bottom: 5px;
        }

        #addFriend {
            padding: 10px;
            background-color: var(--accent-color);
            border: none;
            border-radius: 4px;
            color: white;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.2s ease;
            width: 100%;
        }

        #addFriend:hover {
            background-color: #c0392b;
        }

        /* DM Section */
        #dmSection {
            /* Styles inherited from section */
        }

        #dmChat {
            display: none;
            flex-direction: column;
        }

        #dmMessages {
            height: 150px; 
            overflow-y: scroll; 
            background-color: #fff; 
            padding: 10px; 
            border-radius: 4px; 
            margin-bottom: 10px;
            color: #000;
        }

        #dmMessages div {
            margin-bottom: 5px;
        }

        #dmInput {
            width: 100%; 
            padding: 8px; 
            border: 1px solid var(--input-border); 
            border-radius: 4px;
            margin-bottom: 5px;
        }

        #sendDm {
            padding: 10px;
            background-color: var(--accent-color);
            border: none;
            border-radius: 4px;
            color: white;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.2s ease;
            width: 100%;
        }

        #sendDm:hover {
            background-color: #c0392b;
        }

        /* Avatar Section */
        #avatarSection {
            /* Styles inherited from section */
        }

        #avatarUpload {
            width: 100%;
            margin-bottom: 10px;
        }

        #currentAvatar {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            object-fit: cover;
            border: 2px solid #fff;
        }

        /* Notification Area */
        #notificationArea {
            position: fixed;
            top: 80px;
            right: 20px;
            width: 300px;
            z-index: 2000;
        }

        .notification {
            background-color: rgba(0, 0, 0, 0.8);
            color: #fff;
            padding: 10px;
            margin-bottom: 10px;
            border-radius: 4px;
            opacity: 0.9;
            transition: opacity 0.5s;
        }

        /* Canvas Container */
        #canvasContainer {
            flex: 1;
            margin-left: var(--drawer-width);
            position: relative;
            overflow: hidden;
            background-color: #000000; /* Set black as the background beyond the map */
        }

        /* Grid Canvas */
        #grid {
            width: 100%;
            height: 100%;
            background-color: #ffffff;
            cursor: crosshair;
            display: block;
            position: absolute;
            top: 0;
            left: 0;
            z-index: 1;
        }

        /* Minimap Canvas */
        #minimap {
            position: absolute;
            bottom: 20px;
            right: 20px;
            width: 200px;
            height: 200px;
            border: 2px solid var(--minimap-border);
            background-color: rgba(255, 255, 255, 0.9);
            border-radius: 8px;
            z-index: 500;
            cursor: pointer;
        }

        /* Pixel Info Tooltip */
        #pixelInfo {
            position: absolute;
            background-color: var(--tooltip-background);
            color: var(--tooltip-color);
            padding: 8px;
            border-radius: 4px;
            pointer-events: none;
            white-space: pre-line;
            display: none;
            z-index: 1001;
            font-size: 0.9rem;
            max-width: 200px;
        }

        /* Button Hover Effects */
        .color-btn:hover {
            opacity: 0.8;
            transform: scale(1.1);
        }

        /* Live Cursors */
        .cursor {
            background-color: rgba(255, 0, 0, 0.7);
            width: 10px;
            height: 10px;
            border-radius: 50%;
            position: absolute;
            pointer-events: none;
            transform: translate(-50%, -50%);
        }

        /* Responsive Adjustments */
        @media (max-width: 768px) {
            #drawer {
                width: 250px;
                transform: translateX(-220px);
            }

            #drawer::before {
                width: 36px;
                height: 50px;
                font-size: 1.2rem;
            }

            #minimap {
                width: 150px;
                height: 150px;
                bottom: 15px;
                right: 15px;
            }
        }

        /* Ensure No Overlapping */
        header, #drawer, #canvasContainer, #minimap, #pixelInfo {
            user-select: none;
        }
    </style>
</head>
<body>
    <header>
        <h1 onclick="location.reload();">k/place</h1>
        <div class="user-info">
            <img id="currentAvatarImg" src="/default-avatar.png" alt="Your Avatar">
            <span id="currentUsername">Anonymous</span>
        </div>
    </header>
    <main>
        <!-- Left Side Drawer -->
        <div id="drawer">
            <!-- Tools Section -->
            <section id="toolsSection">
                <h3>Tools</h3>
                <!-- Color Palette -->
                <div class="color-palette" id="colorPalette">
                    <!-- Predefined 16 Colors -->
                    <button class="color-btn selected" style="background-color: #000000;" data-color="#000000"></button>
                    <button class="color-btn" style="background-color: #FFFFFF;" data-color="#FFFFFF"></button>
                    <button class="color-btn" style="background-color: #FF0000;" data-color="#FF0000"></button>
                    <button class="color-btn" style="background-color: #00FF00;" data-color="#00FF00"></button>
                    <button class="color-btn" style="background-color: #0000FF;" data-color="#0000FF"></button>
                    <button class="color-btn" style="background-color: #FFFF00;" data-color="#FFFF00"></button>
                    <button class="color-btn" style="background-color: #00FFFF;" data-color="#00FFFF"></button>
                    <button class="color-btn" style="background-color: #FF00FF;" data-color="#FF00FF"></button>
                    <button class="color-btn" style="background-color: #800000;" data-color="#800000"></button>
                    <button class="color-btn" style="background-color: #808000;" data-color="#808000"></button>
                    <button class="color-btn" style="background-color: #008000;" data-color="#008000"></button>
                    <button class="color-btn" style="background-color: #800080;" data-color="#800080"></button>
                    <button class="color-btn" style="background-color: #008080;" data-color="#008080"></button>
                    <button class="color-btn" style="background-color: #000080;" data-color="#000080"></button>
                    <button class="color-btn" style="background-color: #FFA500;" data-color="#FFA500"></button>
                    <button class="color-btn" style="background-color: #A52A2A;" data-color="#A52A2A"></button>
                </div>
                <!-- Color Picker -->
                <div class="color-picker">
                    <input type="color" id="customColor" value="#000000">
                    <label for="customColor">Custom Color</label>
                </div>
            </section>

            <!-- Username Section -->
            <section id="usernameSection">
                <h3>Username</h3>
                <input type="text" id="username" placeholder="Enter username">
                <button id="setUsername">Set Username</button>
            </section>

            <section id="onlineUsersSection">
                Online users: <span id="onlineUsers">0</span>
            </section>

            <!-- Chat Section -->
            <section id="chatSection">
                <h3>Chat</h3>
                <div id="chatMessages"></div>
                <input type="text" id="chatInput" placeholder="Type a message...">
            </section>

            <!-- Room Section -->
            <section id="roomSection">
                <h3>Rooms</h3>
                <div id="roomList"></div>
                <input type="text" id="newRoomName" placeholder="New room name">
                <button id="createRoom">Create Room</button>
            </section>

            <!-- Friends Section -->
            <section id="friendsSection">
                <h3>Friends</h3>
                <div id="friendList"></div>
                <input type="text" id="friendUsername" placeholder="Friend's username">
                <button id="addFriend">Add Friend</button>
            </section>

            <!-- DM Section -->
            <section id="dmSection">
                <h3>Direct Messages</h3>
                <div id="dmChat">
                    <div id="dmMessages"></div>
                    <input type="text" id="dmInput" placeholder="Type a message...">
                    <button id="sendDm">Send</button>
                </div>
            </section>

            <!-- Avatar Section -->
            <section id="avatarSection">
                <h3>Avatar</h3>
                <input type="file" id="avatarUpload" accept="image/*">
                <img id="currentAvatarImg" src="/default-avatar.png" alt="Your Avatar">
            </section>
        </div>
        
        <!-- Canvas Container -->
        <div id="canvasContainer">
            <canvas id="grid"></canvas>
            <canvas id="minimap"></canvas>
            <div id="pixelInfo"></div>
            <div id="notificationArea"></div>
        </div>
    </main>
    <!-- Include Socket.io client library -->
    <script src="/socket.io/socket.io.js"></script>
    <!-- Include Passport.js client-side if needed -->
    <script>
        const socket = io();

        // Canvas Elements
        const canvas = document.getElementById('grid');
        const ctx = canvas.getContext('2d');
        const bufferCanvas = document.createElement('canvas');
        const bufferCtx = bufferCanvas.getContext('2d');
        const minimap = document.getElementById('minimap');
        const minimapCtx = minimap.getContext('2d');

        // UI Elements
        const colorPalette = document.getElementById('colorPalette');
        const pixelInfo = document.getElementById('pixelInfo');
        const usernameInput = document.getElementById('username');
        const setUsernameBtn = document.getElementById('setUsername');
        const onlineUsersSpan = document.getElementById('onlineUsers');
        const customColorPicker = document.getElementById('customColor');
        const chatMessages = document.getElementById('chatMessages');
        const chatInput = document.getElementById('chatInput');
        const roomList = document.getElementById('roomList');
        const newRoomName = document.getElementById('newRoomName');
        const createRoomBtn = document.getElementById('createRoom');
        const friendList = document.getElementById('friendList');
        const friendUsernameInput = document.getElementById('friendUsername');
        const addFriendBtn = document.getElementById('addFriend');
        const dmChat = document.getElementById('dmChat');
        const dmMessages = document.getElementById('dmMessages');
        const dmInput = document.getElementById('dmInput');
        const sendDmBtn = document.getElementById('sendDm');
        const avatarUpload = document.getElementById('avatarUpload');
        const currentAvatarImg = document.getElementById('currentAvatarImg');
        const notificationArea = document.getElementById('notificationArea');

        // Server-Provided Configuration
        let MAP_WIDTH = 10000;   // Default, will be updated from server
        let MAP_HEIGHT = 10000;  // Default, will be updated from server
        let CHUNK_SIZE = 16;    // Default, will be updated from server

        // Constants
        const pixelSize = 10;
        const predefinedColors = [
            '#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF',
            '#FFFF00', '#00FFFF', '#FF00FF', '#800000', '#808000',
            '#008000', '#800080', '#008080', '#000080', '#FFA500',
            '#A52A2A'
        ];

        // State Variables
        let offsetX = MAP_WIDTH / 2, offsetY = MAP_HEIGHT / 2;
        let zoom = 1;
        let selectedColor = predefinedColors[0];
        let username = 'Anonymous';
        let currentRoom = null;
        let chunks = new Map();
        let cursors = {}; // Store other users' cursor positions

        // Friends and DMs
        let friends = new Set();
        let currentDM = null;

        // Panning Variables
        let isDragging = false;
        let lastX, lastY;

        // Initialize color palette event listeners
        colorPalette.querySelectorAll('.color-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                selectedColor = btn.getAttribute('data-color');
                // Highlight selected color
                colorPalette.querySelectorAll('.color-btn').forEach(b => b.classList.remove('selected'));
                btn.classList.add('selected');
                // Reset custom color picker
                customColorPicker.value = '#000000';
            });
        });

        // Handle custom color picker
        customColorPicker.addEventListener('input', (event) => {
            selectedColor = event.target.value;
            // Remove selection from predefined colors
            colorPalette.querySelectorAll('.color-btn').forEach(b => b.classList.remove('selected'));
        });

        // Resize canvases based on window size
        function resizeCanvas() {
            const drawerWidth = document.getElementById('drawer').offsetWidth;
            const headerHeight = document.querySelector('header').offsetHeight;
            canvas.width = window.innerWidth - drawerWidth;
            canvas.height = window.innerHeight - headerHeight;
            bufferCanvas.width = canvas.width;
            bufferCanvas.height = canvas.height;
            minimap.width = 200;
            minimap.height = 200;
            loadChunks();
            drawGrid();
            drawMinimapViewport();
        }

        // Draw grid lines if desired (optional, can be commented out for better performance)
        function drawGrid() {
            bufferCtx.clearRect(0, 0, bufferCanvas.width, bufferCanvas.height);
            // If you want grid lines, you can implement them here
            // For performance, it's recommended to skip grid lines for large canvases
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(bufferCanvas, 0, 0);
        }

        window.addEventListener('resize', resizeCanvas);

        // Fetch map configuration from server
        socket.on('mapConfig', (config) => {
            MAP_WIDTH = config.MAP_WIDTH;
            MAP_HEIGHT = config.MAP_HEIGHT;
            CHUNK_SIZE = config.CHUNK_SIZE;
            // Center the camera
            offsetX = MAP_WIDTH / 2 - (canvas.width / (2 * pixelSize * zoom));
            offsetY = MAP_HEIGHT / 2 - (canvas.height / (2 * pixelSize * zoom));
            resizeCanvas();
        });

        window.addEventListener('load', () => {
            // Initial resize
            resizeCanvas();
        });

        // Function to convert grid coordinates to screen coordinates
        function gridToScreen(x, y) {
            return {
                screenX: Math.floor((x - offsetX) * pixelSize * zoom),
                screenY: Math.floor((y - offsetY) * pixelSize * zoom)
            };
        }

        // Handle drawing pixels on bufferCanvas
        function drawPixel(chunkKey, x, y, color) {
            const [chunkX, chunkY] = chunkKey.split(',').map(Number);
            const pixelX = chunkX * CHUNK_SIZE + x;
            const pixelY = chunkY * CHUNK_SIZE + y;

            // Check if pixel is within map boundaries
            if (pixelX < 0 || pixelX >= MAP_WIDTH || pixelY < 0 || pixelY >= MAP_HEIGHT) {
                return; // Do not draw pixels outside the map
            }

            bufferCtx.fillStyle = color;
            const pos = gridToScreen(pixelX, pixelY);
            bufferCtx.fillRect(
                pos.screenX,
                pos.screenY,
                Math.ceil(pixelSize * zoom),
                Math.ceil(pixelSize * zoom)
            );
        }

        // Update the main canvas
        function updateCanvas() {
            bufferCtx.clearRect(0, 0, bufferCanvas.width, bufferCanvas.height);

            const visibleChunks = getVisibleChunks();
            visibleChunks.forEach(chunkKey => {
                const chunkData = chunks.get(chunkKey);
                if (chunkData) {
                    Object.values(chunkData).forEach(pixel => {
                        drawPixel(chunkKey, pixel.x, pixel.y, pixel.color);
                    });
                }
            });

            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(bufferCanvas, 0, 0);
            drawMinimap();
            drawMinimapViewport();
        }

        // Update the minimap
        function drawMinimap() {
            minimapCtx.clearRect(0, 0, minimap.width, minimap.height);

            const scaleFactor = minimap.width / MAP_WIDTH; // Scale minimap based on map size
            chunks.forEach((chunkData, chunkKey) => {
                Object.values(chunkData).forEach(pixel => {
                    const pixelX = chunkKey.split(',')[0] * CHUNK_SIZE + pixel.x;
                    const pixelY = chunkKey.split(',')[1] * CHUNK_SIZE + pixel.y;

                    // Ensure pixel is within map boundaries
                    if (pixelX < 0 || pixelX >= MAP_WIDTH || pixelY < 0 || pixelY >= MAP_HEIGHT) {
                        return;
                    }

                    minimapCtx.fillStyle = pixel.color;
                    minimapCtx.fillRect(
                        pixelX * scaleFactor,
                        pixelY * scaleFactor,
                        1,
                        1
                    );
                });
            });
        }

        // Draw the viewport rectangle on minimap
        function drawMinimapViewport() {
            minimapCtx.strokeStyle = 'red';
            minimapCtx.lineWidth = 1;
            const scaleFactor = minimap.width / MAP_WIDTH;

            const viewportWidth = (canvas.width / (pixelSize * zoom)) * scaleFactor;
            const viewportHeight = (canvas.height / (pixelSize * zoom)) * scaleFactor;

            let viewportX = offsetX * scaleFactor;
            let viewportY = offsetY * scaleFactor;

            // Clamp viewport within minimap
            viewportX = Math.max(0, Math.min(viewportX, minimap.width - viewportWidth));
            viewportY = Math.max(0, Math.min(viewportY, minimap.height - viewportHeight));

            minimapCtx.strokeRect(
                viewportX,
                viewportY,
                viewportWidth,
                viewportHeight
            );
        }

        // Determine which chunks are visible based on current offset and zoom
        function getVisibleChunks() {
            const startX = Math.floor(offsetX / CHUNK_SIZE);
            const startY = Math.floor(offsetY / CHUNK_SIZE);
            const endX = Math.ceil((offsetX + canvas.width / (pixelSize * zoom)) / CHUNK_SIZE);
            const endY = Math.ceil((offsetY + canvas.height / (pixelSize * zoom)) / CHUNK_SIZE);

            const visibleChunks = [];
            for (let x = startX; x <= endX; x++) {
                for (let y = startY; y <= endY; y++) {
                    // Ensure chunk coordinates are within map boundaries
                    if (x * CHUNK_SIZE >= 0 && x * CHUNK_SIZE < MAP_WIDTH &&
                        y * CHUNK_SIZE >= 0 && y * CHUNK_SIZE < MAP_HEIGHT) {
                        visibleChunks.push(`${x},${y}`);
                    }
                }
            }
            return visibleChunks;
        }

        // Request chunks from the server
        function loadChunks() {
            const visibleChunks = getVisibleChunks();
            const chunksToLoad = visibleChunks.filter(chunk => !chunks.has(chunk));
            if (chunksToLoad.length > 0) {
                socket.emit('getChunks', { room: currentRoom, chunks: chunksToLoad });
            }
            updateCanvas();
        }

        // Handle incoming chunk data from the server
        socket.on('chunkData', (data) => {
            const { room, chunkData } = data;
            if (room !== currentRoom) return; // Ignore data from other rooms

            Object.entries(chunkData).forEach(([chunkKey, pixels]) => {
                if (!chunks.has(chunkKey)) {
                    chunks.set(chunkKey, {});
                }
                Object.entries(pixels).forEach(([pixelKey, pixelData]) => {
                    const [x, y] = pixelKey.split(',').map(Number);
                    // Ensure pixel is within map boundaries
                    const pixelX = chunkKey.split(',')[0] * CHUNK_SIZE + x;
                    const pixelY = chunkKey.split(',')[1] * CHUNK_SIZE + y;
                    if (pixelX < 0 || pixelX >= MAP_WIDTH || pixelY < 0 || pixelY >= MAP_HEIGHT) {
                        return;
                    }
                    chunks.get(chunkKey)[`${x},${y}`] = { x, y, color: pixelData.color, username: pixelData.username, time: pixelData.time };
                });
            });
            updateCanvas();
        });

        // Handle pixel updates from other users
        socket.on('pixelUpdate', (data) => {
            const { room, chunkKey, x, y, color } = data;
            if (room !== currentRoom) return; // Ignore updates from other rooms

            // Ensure pixel is within map boundaries
            const pixelX = chunkKey.split(',')[0] * CHUNK_SIZE + x;
            const pixelY = chunkKey.split(',')[1] * CHUNK_SIZE + y;
            if (pixelX < 0 || pixelX >= MAP_WIDTH || pixelY < 0 || pixelY >= MAP_HEIGHT) {
                return; // Do not process pixels outside the map
            }

            if (!chunks.has(chunkKey)) {
                chunks.set(chunkKey, {});
            }
            chunks.get(chunkKey)[`${x},${y}`] = { x, y, color, username: 'Anonymous', time: Date.now() };
            drawPixel(chunkKey, x, y, color);
            updateCanvas();
        });

        // Update online users count
        socket.on('onlineUsers', (count) => {
            onlineUsersSpan.textContent = count;
        });

        // Place a pixel on the canvas
        function placePixel(event) {
            if (!currentRoom) {
                newRoomName.value = 'default';
                createRoomBtn.click();
            }
            const rect = canvas.getBoundingClientRect();
            const screenX = event.clientX - rect.left;
            const screenY = event.clientY - rect.top;

            // Convert screen coordinates to world coordinates
            const worldX = (screenX / (pixelSize * zoom)) + offsetX;
            const worldY = (screenY / (pixelSize * zoom)) + offsetY;

            // Convert world coordinates to grid coordinates
            const x = Math.floor(worldX);
            const y = Math.floor(worldY);

            // Ensure pixel is within map boundaries
            if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) {
                alert('Cannot place pixel outside the map boundaries!');
                return;
            }

            socket.emit('placePixel', { room: currentRoom, x, y, color: selectedColor, username });
            loadChunks();
        }

        // Handle mouse down events for placing pixels and panning
        canvas.addEventListener('mousedown', (event) => {
            if (event.button === 0) { // Left mouse button
                placePixel(event);
                canvas.addEventListener('mousemove', placePixel);
            } else if (event.button === 2) { // Right mouse button
                isDragging = true;
                lastX = event.clientX;
                lastY = event.clientY;
                canvas.style.cursor = 'grabbing';
            }
        });

        // Handle mouse up events
        canvas.addEventListener('mouseup', (event) => {
            if (event.button === 0) { // Left mouse button
                canvas.removeEventListener('mousemove', placePixel);
            } else if (event.button === 2) { // Right mouse button
                isDragging = false;
                canvas.style.cursor = 'crosshair';
            }
        });

        // Prevent context menu on right-click
        canvas.addEventListener('contextmenu', (event) => {
            event.preventDefault();
        });

        // Handle mouse move events for panning and displaying pixel info
        canvas.addEventListener('mousemove', (event) => {
            if (isDragging) {
                const deltaX = event.clientX - lastX;
                const deltaY = event.clientY - lastY;
                offsetX -= deltaX / (pixelSize * zoom);
                offsetY -= deltaY / (pixelSize * zoom);

                // Clamp offsets to map boundaries
                const maxOffsetX = MAP_WIDTH - canvas.width / (pixelSize * zoom);
                const maxOffsetY = MAP_HEIGHT - canvas.height / (pixelSize * zoom);
                offsetX = Math.max(0, Math.min(offsetX, maxOffsetX));
                offsetY = Math.max(0, Math.min(offsetY, maxOffsetY));

                lastX = event.clientX;
                lastY = event.clientY;

                loadChunks();
                return; // Skip pixel info when panning
            }

            const rect = canvas.getBoundingClientRect();
            const screenX = event.clientX - rect.left;
            const screenY = event.clientY - rect.top;

            // Convert screen coordinates to world coordinates
            const worldX = (screenX / (pixelSize * zoom)) + offsetX;
            const worldY = (screenY / (pixelSize * zoom)) + offsetY;

            // Convert world coordinates to grid coordinates
            const x = Math.floor(worldX);
            const y = Math.floor(worldY);

            // Ensure pixel is within map boundaries before requesting info
            if (x >= 0 && x < MAP_WIDTH && y >= 0 && y < MAP_HEIGHT) {
                socket.emit('getPixelInfo', { room: currentRoom, x, y });
            } else {
                pixelInfo.style.display = 'none';
                return;
            }

            // Position the pixelInfo tooltip near the cursor
            pixelInfo.style.left = `${event.clientX + 10}px`;
            pixelInfo.style.top = `${event.clientY + 10}px`;
        });

        // Handle zooming with the mouse wheel
        canvas.addEventListener('wheel', (event) => {
            event.preventDefault();
            const rect = canvas.getBoundingClientRect();
            const mouseX = event.clientX - rect.left;
            const mouseY = event.clientY - rect.top;

            // Convert screen coordinates to world coordinates before zoom
            const worldX = (mouseX / (pixelSize * zoom)) + offsetX;
            const worldY = (mouseY / (pixelSize * zoom)) + offsetY;

            // Determine zoom factor
            const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
            const newZoom = zoom * zoomFactor;
            zoom = Math.max(0.2, Math.min(newZoom, 50)); // Clamp zoom

            // Adjust offset to keep the zoom centered on the mouse position
            offsetX = worldX - (mouseX / (pixelSize * zoom));
            offsetY = worldY - (mouseY / (pixelSize * zoom));

            // Clamp offsets to map boundaries after zoom
            const maxOffsetX = MAP_WIDTH - canvas.width / (pixelSize * zoom);
            const maxOffsetY = MAP_HEIGHT - canvas.height / (pixelSize * zoom);
            offsetX = Math.max(0, Math.min(offsetX, maxOffsetX));
            offsetY = Math.max(0, Math.min(offsetY, maxOffsetY));

            loadChunks();
        });

        function capitalizeString(str) {
    if (str.length === 0) return str;
    return str.charAt(0).toUpperCase() + str.slice(1);
}

        // Handle incoming pixel info
        socket.on('pixelInfo', (info) => {
            if (info && info.room === currentRoom) {
                pixelInfo.style.display = 'block';
                const timeString = new Date(info.time).toLocaleString();
                if (info.color === '#FFFFFF') {
                    pixelInfo.style.display = 'none';
                }
                pixelInfo.textContent = `${info.username}: ${capitalizeString(info.color)}\n[${info.x}, ${info.y}]\n(${timeString})`;
            } else {
                pixelInfo.style.display = 'none';
            }
        });

        // Handle setting the username
        setUsernameBtn.addEventListener('click', () => {
            const inputUsername = usernameInput.value.trim();
            if (inputUsername) {
                username = inputUsername;
                socket.emit('setUsername', username);
                usernameInput.value = '';
                document.getElementById('currentUsername').textContent = username;
                addNotification(`Username set to ${username}`);
            }
        });

        // Optionally, set username on Enter key
        usernameInput.addEventListener('keypress', (event) => {
            if (event.key === 'Enter') {
                setUsernameBtn.click();
            }
        });

        // Handle sending chat messages
        chatInput.addEventListener('keypress', (event) => {
            if (event.key === 'Enter' && chatInput.value.trim() !== '') {
                socket.emit('chatMessage', { room: currentRoom, username, message: chatInput.value.trim() });
                chatInput.value = '';
            }
        });

        // Receive and display chat messages
        socket.on('chatMessage', (data) => {
            if (data.room !== currentRoom) return;
            const messageEl = document.createElement('div');
            messageEl.innerHTML = `<strong>${data.username}:</strong> ${data.message}`;
            chatMessages.appendChild(messageEl);
            chatMessages.scrollTop = chatMessages.scrollHeight;
        });

        // Handle creating a new room
        createRoomBtn.addEventListener('click', () => {
            const roomName = newRoomName.value.trim();
            if (roomName !== '') {
                socket.emit('joinRoom', roomName);
                newRoomName.value = '';
            }
        });

        // Receive confirmation of room join
        socket.on('joinedRoom', (roomName) => {
            addNotification(`Joined room: ${roomName}`);
            // Clear existing chunks and load new ones
            chunks.clear();
            loadChunks();
            currentRoom = roomName;
            // Clear chat messages
            chatMessages.innerHTML = '';
            // Update current room display if necessary
        });

        // Receive room list
        socket.on('roomList', (rooms) => {
            roomList.innerHTML = '';
            rooms.forEach(room => {
                const roomEl = document.createElement('div');
                roomEl.textContent = room;
                roomEl.addEventListener('click', () => {
                    socket.emit('joinRoom', room);
                });
                roomList.appendChild(roomEl);
            });
        });

        // Populate room list on initial load
        socket.emit('requestRoomList');

        // Handle adding friends
        addFriendBtn.addEventListener('click', () => {
            const friendUsername = friendUsernameInput.value.trim();
            if (friendUsername !== '') {
                socket.emit('addFriend', friendUsername);
                friendUsernameInput.value = '';
            }
        });

        // Receive confirmation of friend addition
        socket.on('friendAdded', (friendUsername) => {
            const friendEl = document.createElement('div');
            const statusEl = document.createElement('span');
            statusEl.classList.add('friend-status');
            statusEl.style.width = '10px';
            statusEl.style.height = '10px';
            statusEl.style.borderRadius = '50%';
            statusEl.style.backgroundColor = 'red'; // Offline by default
            statusEl.style.marginRight = '5px';
            friendEl.appendChild(statusEl);
            const nameEl = document.createElement('span');
            nameEl.textContent = friendUsername;
            friendEl.appendChild(nameEl);
            friendList.appendChild(friendEl);
        });

        // Handle receiving friend online status
        socket.on('friendOnline', (friendUsername) => {
            const friends = Array.from(friendList.children);
            friends.forEach(friendEl => {
                if (friendEl.textContent.includes(friendUsername)) {
                    const statusEl = friendEl.querySelector('.friend-status');
                    statusEl.style.backgroundColor = 'green';
                }
            });
        });

        // Handle receiving friend offline status
        socket.on('friendOffline', (friendUsername) => {
            const friends = Array.from(friendList.children);
            friends.forEach(friendEl => {
                if (friendEl.textContent.includes(friendUsername)) {
                    const statusEl = friendEl.querySelector('.friend-status');
                    statusEl.style.backgroundColor = 'red';
                }
            });
        });

        // Open DM when clicking on a friend
        friendList.addEventListener('click', (event) => {
            const friendUsername = event.target.textContent;
            if (friendUsername) {
                openDM(friendUsername);
            }
        });

        // Function to open DM chat with a friend
        function openDM(friendUsername) {
            dmChat.style.display = 'flex';
            window.currentDM = friendUsername;
            dmMessages.innerHTML = '';
            addNotification(`Chatting with ${friendUsername}`);
        }

        // Send DM
        sendDmBtn.addEventListener('click', () => {
            const message = dmInput.value.trim();
            if (message && window.currentDM) {
                socket.emit('dm', { toUsername: window.currentDM, message });
                // Display sent message in DM chat
                const msgEl = document.createElement('div');
                msgEl.innerHTML = `<strong>You:</strong> ${message}`;
                dmMessages.appendChild(msgEl);
                dmInput.value = '';
            }
        });

        // Receive DMs
        socket.on('dm', (data) => {
            const { from, message } = data;
            // Display received message
            const msgEl = document.createElement('div');
            msgEl.innerHTML = `<strong>${from}:</strong> ${message}`;
            dmMessages.appendChild(msgEl);
            dmChat.style.display = 'flex'; // Ensure DM chat is visible
            addNotification(`New DM from ${from}`);
        });

        // Handle avatar upload
        avatarUpload.addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function(e) {
                    const avatarData = e.target.result;
                    socket.emit('updateAvatar', avatarData);
                    currentAvatarImg.src = avatarData;
                };
                reader.readAsDataURL(file);
            }
        });

        // Receive other users' avatars
        socket.on('userAvatar', (data) => {
            const { socketId, avatar } = data;
            // Update avatar display logic as needed
            // For example, associate avatars with cursors or user lists
            // Implementation depends on frontend structure
        });

        // Live Cursor Positions
        canvas.addEventListener('mousemove', (event) => {
            if (!isDragging) {
                const rect = canvas.getBoundingClientRect();
                const x = event.clientX - rect.left;
                const y = event.clientY - rect.top;
                socket.emit('cursorMove', { room: currentRoom, x, y });
            }
        });

        // Receive other users' cursor movements
        socket.on('cursorMove', (data) => {
            const { socketId, room, x, y } = data;
            if (room !== currentRoom) return;

            if (!cursors[socketId]) {
                // Create a cursor element
                const cursorEl = document.createElement('div');
                cursorEl.classList.add('cursor');
                document.getElementById('canvasContainer').appendChild(cursorEl);
                cursors[socketId] = cursorEl;
            }
            // Update cursor position
            cursors[socketId].style.left = `${x}px`;
            cursors[socketId].style.top = `${y}px`;
        });

        // Remove cursor when user disconnects
        socket.on('cursorDisconnect', (data) => {
            const { socketId, room } = data;
            if (room !== currentRoom) return;
            if (cursors[socketId]) {
                cursors[socketId].remove();
                delete cursors[socketId];
            }
        });

        // Handle incoming pixel info
        socket.on('pixelInfo', (info) => {
            if (info && info.room === currentRoom) {
                pixelInfo.style.display = 'block';
                const timeString = new Date(info.time).toLocaleString();
                pixelInfo.textContent = `${info.username}: ${info.color}\n[${info.x}, ${info.y}]\n(${timeString})`;
            } else {
                pixelInfo.style.display = 'none';
            }
        });

        // Handle user online/offline status
        socket.on('userOnline', (username) => {
            // Update friend status if necessary
            const friendsArray = Array.from(friendList.children);
            friendsArray.forEach(friendEl => {
                if (friendEl.textContent.includes(username)) {
                    const statusEl = friendEl.querySelector('.friend-status');
                    statusEl.style.backgroundColor = 'green';
                }
            });
        });

        socket.on('userOffline', (username) => {
            // Update friend status if necessary
            const friendsArray = Array.from(friendList.children);
            friendsArray.forEach(friendEl => {
                if (friendEl.textContent.includes(username)) {
                    const statusEl = friendEl.querySelector('.friend-status');
                    statusEl.style.backgroundColor = 'red';
                }
            });
        });

        // Add notification to notification area
        function addNotification(message) {
            const notifEl = document.createElement('div');
            notifEl.classList.add('notification');
            notifEl.textContent = message;
            notificationArea.appendChild(notifEl);
            // Automatically remove after 5 seconds
            setTimeout(() => {
                notifEl.remove();
            }, 5000);
        }

        // Receive notifications
        socket.on('notification', (data) => {
            const { type, message } = data;
            addNotification(message);
        });

        // Ensure initial canvas renders any existing pixels
        function initialize() {
            resizeCanvas();
        }

        initialize();

        // Minimap Interactivity: Click to navigate
        minimap.addEventListener('click', (event) => {
            const rect = minimap.getBoundingClientRect();
            const clickX = event.clientX - rect.left;
            const clickY = event.clientY - rect.top;

            const mapClickX = clickX / minimap.width * MAP_WIDTH;
            const mapClickY = clickY / minimap.height * MAP_HEIGHT;

            // Center the main canvas on the clicked point
            offsetX = mapClickX - (canvas.width / (2 * pixelSize * zoom));
            offsetY = mapClickY - (canvas.height / (2 * pixelSize * zoom));

            // Clamp offsets
            const maxOffsetX = MAP_WIDTH - canvas.width / (pixelSize * zoom);
            const maxOffsetY = MAP_HEIGHT - canvas.height / (pixelSize * zoom);
            offsetX = Math.max(0, Math.min(offsetX, maxOffsetX));
            offsetY = Math.max(0, Math.min(offsetY, maxOffsetY));

            loadChunks();
        });

        // Disconnect Handling
        socket.on('disconnect', () => {
            onlineUsersSpan.textContent = 0;
            addNotification('You have been disconnected.');
        });
        socket.emit('joinRoom', 'default');
        socket.emit('requestRoomList');
        setTimeout(() => {
            loadChunks();
        }, 500);
    </script>
</body>
</html>

improve styles, DON'T touch javascript unless adding effects or animations. Respond with modified styles and new scripts.