#ifndef INDEX_H
#define INDEX_H

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Byun CAN Logger</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 10px;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            text-align: center;
        }
        .header h1 { font-size: 1.8em; margin-bottom: 5px; }
        .header p { opacity: 0.9; font-size: 0.9em; }
        .nav {
            background: #2c3e50;
            padding: 10px;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }
        .nav a {
            color: white;
            text-decoration: none;
            padding: 10px 15px;
            border-radius: 5px;
            transition: all 0.3s;
            font-size: 0.9em;
            white-space: nowrap;
        }
        .nav a:hover { background: #34495e; }
        .nav a.active { background: #3498db; }
        .content { padding: 15px; }
        
        .time-sync-banner {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
            color: white;
            padding: 15px 20px;
            border-radius: 10px;
            margin-bottom: 20px;
            box-shadow: 0 4px 15px rgba(240, 147, 251, 0.4);
        }
        .time-sync-info {
            display: flex;
            gap: 20px;
            align-items: center;
            flex-wrap: wrap;
            margin-bottom: 15px;
        }
        .time-info-item {
            display: flex;
            flex-direction: column;
            gap: 3px;
        }
        .time-label {
            font-size: 0.75em;
            opacity: 0.9;
            font-weight: 600;
        }
        .time-value {
            font-family: 'Courier New', monospace;
            font-size: 1.1em;
            font-weight: 700;
        }
        
        .rtc-panel {
            background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
            padding: 15px;
            border-radius: 10px;
            margin-top: 15px;
            border-left: 4px solid #667eea;
        }
        .rtc-panel h3 {
            color: #333;
            margin-bottom: 10px;
            font-size: 1em;
        }
        .rtc-controls {
            display: flex;
            gap: 10px;
            align-items: center;
            flex-wrap: wrap;
        }
        .rtc-time {
            flex: 1;
            min-width: 200px;
            padding: 10px 15px;
            background: white;
            border-radius: 8px;
            font-family: 'Courier New', monospace;
            font-size: 1.1em;
            font-weight: 600;
            color: #667eea;
            text-align: center;
        }
        .rtc-status {
            padding: 6px 12px;
            background: white;
            border-radius: 5px;
            font-size: 0.85em;
            font-weight: 600;
        }
        .rtc-status.available { color: #11998e; }
        .rtc-status.unavailable { color: #e74c3c; }
        
        .btn-time-sync, .btn-rtc {
            background: white;
            color: #f5576c;
            padding: 10px 20px;
            border: none;
            border-radius: 8px;
            font-size: 0.9em;
            font-weight: 700;
            cursor: pointer;
            transition: all 0.3s;
            box-shadow: 0 3px 10px rgba(0,0,0,0.2);
        }
        .btn-rtc {
            color: #667eea;
        }
        .btn-time-sync:hover, .btn-rtc:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 20px rgba(0,0,0,0.3);
        }
        
        .status-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 10px;
            margin-bottom: 20px;
        }
        .status-card {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 15px;
            border-radius: 10px;
            text-align: center;
        }
        .status-card h3 { font-size: 0.75em; opacity: 0.9; margin-bottom: 8px; }
        .status-card .value { font-size: 1.5em; font-weight: bold; word-break: break-all; }
        .status-on { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%) !important; }
        .status-off { background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%) !important; }
        .control-panel {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 20px;
        }
        .control-row {
            display: flex;
            gap: 10px;
            align-items: center;
            flex-wrap: wrap;
            margin-bottom: 10px;
        }
        .control-row:last-child { margin-bottom: 0; }
        label { font-weight: 600; color: #333; font-size: 0.9em; }
        select, button {
            padding: 8px 15px;
            border: none;
            border-radius: 5px;
            font-size: 0.9em;
            cursor: pointer;
            transition: all 0.3s;
        }
        select {
            background: white;
            border: 2px solid #667eea;
            color: #333;
        }
        button {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            font-weight: 600;
        }
        button:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
        button:active { transform: translateY(0); }
        .can-table-container {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 10px;
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
        }
        table {
            width: 100%;
            min-width: 500px;
            border-collapse: collapse;
            background: white;
            border-radius: 8px;
            overflow: hidden;
        }
        th {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 10px 8px;
            text-align: left;
            font-weight: 600;
            font-size: 0.85em;
        }
        td {
            padding: 8px;
            border-bottom: 1px solid #e9ecef;
            font-family: 'Courier New', monospace;
            font-size: 0.8em;
        }
        tr:hover { background: #f8f9fa; }
        .flash-row {
            animation: flashAnimation 0.3s ease-in-out;
        }
        @keyframes flashAnimation {
            0%, 100% { background-color: transparent; }
            50% { background-color: #fff3cd; }
        }
        .mono { font-family: 'Courier New', monospace; }
        h2 {
            color: #333;
            margin: 20px 0 15px 0;
            padding-bottom: 8px;
            border-bottom: 3px solid #667eea;
            font-size: 1.3em;
        }
        .file-list {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 15px;
        }
        .file-item {
            background: white;
            padding: 12px;
            margin-bottom: 8px;
            border-radius: 8px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: all 0.3s;
            flex-wrap: wrap;
            gap: 10px;
        }
        .file-item:hover { transform: translateX(5px); box-shadow: 0 3px 10px rgba(0,0,0,0.1); }
        .file-name { font-weight: 600; color: #333; font-size: 0.9em; }
        .file-size { color: #666; margin-left: 10px; font-size: 0.85em; }
        .download-btn {
            padding: 6px 12px;
            font-size: 0.85em;
        }
        
        @media (max-width: 768px) {
            body { padding: 5px; }
            .header h1 { font-size: 1.5em; }
            .header p { font-size: 0.85em; }
            .content { padding: 10px; }
            .status-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
            .status-card { padding: 10px; }
            .status-card h3 { font-size: 0.7em; }
            .status-card .value { font-size: 1.2em; }
            h2 { font-size: 1.1em; }
            .nav a { padding: 8px 12px; font-size: 0.85em; }
            table { min-width: 400px; }
            th, td { padding: 6px 4px; font-size: 0.75em; }
            .time-sync-banner {
                padding: 12px 15px;
            }
            .time-sync-info {
                gap: 10px;
            }
            .time-value {
                font-size: 1em;
            }
            .rtc-controls {
                flex-direction: column;
                align-items: stretch;
            }
            .rtc-time {
                min-width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Byun CAN Logger</h1>
            <p>Real-time CAN Bus Monitor & Data Logger with RTC</p>
        </div>
        
        <div class="nav">
            <a href="/" class="active">Monitor</a>
            <a href="/transmit">Transmit</a>
            <a href="/graph">Graph</a>
        </div>
        
        <div class="content">
            <div class="time-sync-banner">
                <div class="time-sync-info">
                    <div class="time-info-item">
                        <span class="time-label">⏰ 시간 동기화 상태</span>
                        <span class="time-value" id="sync-status">대기 중...</span>
                    </div>
                    <div class="time-info-item">
                        <span class="time-label">🕐 현재 시간</span>
                        <span class="time-value" id="current-time">--:--:--</span>
                    </div>
                </div>
                <button class="btn-time-sync" onclick="syncTime()">⏰ 웹 시간 동기화</button>
                
                <div class="rtc-panel">
                    <h3>🕰️ RTC DS3231 모듈</h3>
                    <div class="rtc-controls">
                        <div class="rtc-time" id="rtc-time">--:--:--</div>
                        <span class="rtc-status unavailable" id="rtc-status">확인 중...</span>
                        <button class="btn-rtc" onclick="getRTCTime()">📖 RTC 읽기</button>
                        <button class="btn-rtc" onclick="setRTCTime()">📝 RTC 설정</button>
                        <button class="btn-rtc" onclick="syncFromRTC()">🔄 RTC→시스템</button>
                    </div>
                </div>
            </div>
            
            <div class="status-grid">
                <div class="status-card" id="logging-status">
                    <h3>LOGGING</h3>
                    <div class="value">OFF</div>
                </div>
                <div class="status-card" id="sd-status">
                    <h3>SD CARD</h3>
                    <div class="value">NOT READY</div>
                </div>
                <div class="status-card">
                    <h3>MESSAGES</h3>
                    <div class="value" id="msg-count">0</div>
                </div>
                <div class="status-card">
                    <h3>SPEED</h3>
                    <div class="value" id="msg-speed">0/s</div>
                </div>
                <div class="status-card" id="time-sync-card">
                    <h3>TIME SYNC</h3>
                    <div class="value" id="sync-count">0</div>
                </div>
                <div class="status-card" id="file-status">
                    <h3>CURRENT FILE</h3>
                    <div class="value" id="current-file" style="font-size: 0.85em;">-</div>
                </div>
            </div>
            
            <div class="control-panel">
                <h2>Control Panel</h2>
                <div class="control-row">
                    <label for="can-speed">CAN Speed:</label>
                    <select id="can-speed">
                        <option value="0">125 Kbps</option>
                        <option value="1">250 Kbps</option>
                        <option value="2">500 Kbps</option>
                        <option value="3" selected>1 Mbps</option>
                    </select>
                    <button onclick="setCanSpeed()">Apply</button>
                    <span id="speed-status" style="color: #11998e; font-size: 0.85em; font-weight: 600;"></span>
                </div>
                <div class="control-row">
                    <button onclick="startLogging()" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);">Start Logging</button>
                    <button onclick="stopLogging()" style="background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);">Stop Logging</button>
                    <button onclick="refreshFiles()">Refresh Files</button>
                    <button onclick="clearMessages()">Clear Display</button>
                </div>
            </div>
            
            <h2>CAN Messages (by ID)</h2>
            <div class="can-table-container">
                <table>
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>DLC</th>
                            <th>Data</th>
                            <th>Count</th>
                            <th>Time(ms)</th>
                        </tr>
                    </thead>
                    <tbody id="can-messages"></tbody>
                </table>
            </div>
            
            <h2>Log Files</h2>
            <div class="file-list" id="file-list">
                <p style="text-align: center; color: #666; font-size: 0.9em;">Loading...</p>
            </div>
        </div>
    </div>
    
    <script>
        let ws;
        let reconnectInterval;
        let canMessages = {};
        let messageOrder = [];
        let lastMessageData = {};
        let rtcAvailable = false;
        
        const speedNames = ['125 Kbps', '250 Kbps', '500 Kbps', '1 Mbps'];
        
        // 현재 시간 업데이트
        function updateCurrentTime() {
            const now = new Date();
            const timeStr = now.getHours().toString().padStart(2, '0') + ':' +
                           now.getMinutes().toString().padStart(2, '0') + ':' +
                           now.getSeconds().toString().padStart(2, '0');
            document.getElementById('current-time').textContent = timeStr;
        }
        
        setInterval(updateCurrentTime, 1000);
        updateCurrentTime();
        
        // 웹 시간 동기화
        function syncTime() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                const timestamp = Date.now();
                ws.send(JSON.stringify({
                    cmd: 'syncTime',
                    timestamp: timestamp
                }));
                
                document.getElementById('sync-status').textContent = '동기화 중...';
                
                setTimeout(() => {
                    const now = new Date();
                    const dateStr = now.getFullYear() + '-' + 
                                   (now.getMonth() + 1).toString().padStart(2, '0') + '-' +
                                   now.getDate().toString().padStart(2, '0') + ' ' +
                                   now.getHours().toString().padStart(2, '0') + ':' +
                                   now.getMinutes().toString().padStart(2, '0') + ':' +
                                   now.getSeconds().toString().padStart(2, '0');
                    document.getElementById('sync-status').textContent = '✓ ' + dateStr;
                }, 200);
                
                console.log('웹 시간 동기화 전송:', new Date(timestamp).toLocaleString());
            } else {
                alert('WebSocket이 연결되지 않았습니다!');
            }
        }
        
        // RTC 시간 읽기
        function getRTCTime() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.send(JSON.stringify({ cmd: 'getRTC' }));
                console.log('RTC 시간 요청');
            } else {
                alert('WebSocket이 연결되지 않았습니다!');
            }
        }
        
        // RTC에 현재 웹 시간 설정
        function setRTCTime() {
            if (!rtcAvailable) {
                alert('RTC를 사용할 수 없습니다!');
                return;
            }
            
            if (!confirm('현재 웹 브라우저 시간을 RTC에 설정하시겠습니까?')) {
                return;
            }
            
            if (ws && ws.readyState === WebSocket.OPEN) {
                const timestamp = Date.now();
                ws.send(JSON.stringify({
                    cmd: 'setRTC',
                    timestamp: timestamp
                }));
                
                console.log('RTC 시간 설정:', new Date(timestamp).toLocaleString());
                
                // 설정 후 읽기
                setTimeout(() => { getRTCTime(); }, 500);
            } else {
                alert('WebSocket이 연결되지 않았습니다!');
            }
        }
        
        // RTC에서 시스템 시간 동기화
        function syncFromRTC() {
            if (!rtcAvailable) {
                alert('RTC를 사용할 수 없습니다!');
                return;
            }
            
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.send(JSON.stringify({ cmd: 'syncFromRTC' }));
                console.log('RTC→시스템 동기화 요청');
                
                setTimeout(() => { getRTCTime(); }, 500);
            } else {
                alert('WebSocket이 연결되지 않았습니다!');
            }
        }
        
        function initWebSocket() {
            ws = new WebSocket('ws://' + window.location.hostname + ':81');
            
            ws.onopen = function() {
                console.log('WebSocket connected');
                clearInterval(reconnectInterval);
                setTimeout(() => { refreshFiles(); }, 500);
                setTimeout(() => { getRTCTime(); }, 1000);
            };
            
            ws.onclose = function() {
                console.log('WebSocket disconnected');
                reconnectInterval = setInterval(initWebSocket, 3000);
                document.getElementById('sync-status').textContent = '연결 끊김';
            };
            
            ws.onmessage = function(event) {
                const data = JSON.parse(event.data);
                
                if (data.type === 'status') {
                    updateStatus(data);
                } else if (data.type === 'can') {
                    addCanMessage(data);
                } else if (data.type === 'canBatch') {
                    updateCanBatch(data.messages);
                } else if (data.type === 'files') {
                    if (data.error) {
                        document.getElementById('file-list').innerHTML = 
                            '<p style="text-align: center; color: #e74c3c; font-size: 0.9em;">Error: ' + data.error + '</p>';
                    } else {
                        updateFileList(data.files);
                    }
                } else if (data.type === 'timeSyncStatus') {
                    if (data.synchronized) {
                        document.getElementById('sync-count').textContent = data.syncCount;
                        const card = document.getElementById('time-sync-card');
                        card.classList.add('status-on');
                        card.classList.remove('status-off');
                    }
                    
                    if (data.rtcAvailable !== undefined) {
                        rtcAvailable = data.rtcAvailable;
                        updateRTCStatus(rtcAvailable);
                    }
                } else if (data.type === 'rtcTime') {
                    document.getElementById('rtc-time').textContent = data.time;
                    
                    if (data.available !== undefined) {
                        rtcAvailable = data.available;
                        updateRTCStatus(rtcAvailable);
                    }
                }
            };
        }
        
        function updateRTCStatus(available) {
            const statusEl = document.getElementById('rtc-status');
            if (available) {
                statusEl.textContent = '✓ 사용 가능';
                statusEl.className = 'rtc-status available';
            } else {
                statusEl.textContent = '✗ 사용 불가';
                statusEl.className = 'rtc-status unavailable';
            }
        }
        
        function updateStatus(data) {
            const loggingCard = document.getElementById('logging-status');
            const sdCard = document.getElementById('sd-status');
            const fileCard = document.getElementById('file-status');
            const timeSyncCard = document.getElementById('time-sync-card');
            
            if (data.logging) {
                loggingCard.classList.add('status-on');
                loggingCard.classList.remove('status-off');
                loggingCard.querySelector('.value').textContent = 'ON';
            } else {
                loggingCard.classList.add('status-off');
                loggingCard.classList.remove('status-on');
                loggingCard.querySelector('.value').textContent = 'OFF';
            }
            
            if (data.sdReady) {
                sdCard.classList.add('status-on');
                sdCard.classList.remove('status-off');
                sdCard.querySelector('.value').textContent = 'READY';
            } else {
                sdCard.classList.add('status-off');
                sdCard.classList.remove('status-on');
                sdCard.querySelector('.value').textContent = 'NOT READY';
            }
            
            if (data.timeSync) {
                timeSyncCard.classList.add('status-on');
                timeSyncCard.classList.remove('status-off');
            } else {
                timeSyncCard.classList.add('status-off');
                timeSyncCard.classList.remove('status-on');
            }
            
            if (data.syncCount !== undefined) {
                document.getElementById('sync-count').textContent = data.syncCount;
            }
            
            if (data.rtcAvailable !== undefined) {
                rtcAvailable = data.rtcAvailable;
                updateRTCStatus(rtcAvailable);
            }
            
            if (data.currentFile && data.currentFile !== '') {
                fileCard.classList.add('status-on');
                fileCard.classList.remove('status-off');
                document.getElementById('current-file').textContent = data.currentFile;
            } else {
                fileCard.classList.remove('status-on', 'status-off');
                document.getElementById('current-file').textContent = '-';
            }
            
            document.getElementById('msg-count').textContent = data.msgCount.toLocaleString();
            document.getElementById('msg-speed').textContent = data.msgSpeed + '/s';
        }
        
        function addCanMessage(data) {
            const canId = data.id;
            if (!canMessages[canId]) {
                messageOrder.push(canId);
            }
            canMessages[canId] = {
                timestamp: data.timestamp,
                dlc: data.dlc,
                data: data.data,
                updateCount: data.count
            };
        }
        
        function updateCanBatch(messages) {
            messages.forEach(msg => {
                const canId = msg.id;
                if (!canMessages[canId]) {
                    messageOrder.push(canId);
                }
                canMessages[canId] = {
                    timestamp: msg.timestamp,
                    dlc: msg.dlc,
                    data: msg.data,
                    updateCount: msg.count
                };
            });
            updateCanTable();
        }
        
        function updateCanTable() {
            const tbody = document.getElementById('can-messages');
            const existingRows = new Map();
            Array.from(tbody.rows).forEach(row => {
                existingRows.set(row.dataset.canId, row);
            });
            
            messageOrder.forEach(canId => {
                const msg = canMessages[canId];
                let row = existingRows.get(canId);
                
                const prevData = lastMessageData[canId];
                const hasChanged = !prevData || 
                    prevData.data !== msg.data || 
                    prevData.dlc !== msg.dlc ||
                    prevData.timestamp !== msg.timestamp;
                
                if (row) {
                    row.cells[1].textContent = msg.dlc;
                    row.cells[2].textContent = msg.data;
                    row.cells[3].textContent = msg.updateCount;
                    row.cells[4].textContent = msg.timestamp;
                    
                    if (hasChanged) {
                        row.classList.add('flash-row');
                        setTimeout(() => row.classList.remove('flash-row'), 300);
                    }
                } else {
                    row = tbody.insertRow();
                    row.dataset.canId = canId;
                    row.innerHTML = 
                        '<td class="mono">0x' + canId + '</td>' +
                        '<td>' + msg.dlc + '</td>' +
                        '<td class="mono">' + msg.data + '</td>' +
                        '<td>' + msg.updateCount + '</td>' +
                        '<td>' + msg.timestamp + '</td>';
                    row.classList.add('flash-row');
                    setTimeout(() => row.classList.remove('flash-row'), 300);
                }
                
                lastMessageData[canId] = {
                    data: msg.data,
                    dlc: msg.dlc,
                    timestamp: msg.timestamp,
                    updateCount: msg.updateCount
                };
            });
        }
        
        function updateFileList(files) {
            const fileList = document.getElementById('file-list');
            
            if (!files || files.length === 0) {
                fileList.innerHTML = '<p style="text-align: center; color: #666; font-size: 0.9em;">No log files</p>';
                return;
            }
            
            files.sort((a, b) => {
                return b.name.localeCompare(a.name);
            });
            
            fileList.innerHTML = '';
            files.forEach(file => {
                const fileItem = document.createElement('div');
                fileItem.className = 'file-item';
                fileItem.innerHTML = 
                    '<div style="flex: 1; min-width: 0;">' +
                        '<span class="file-name">' + file.name + '</span>' +
                        '<span class="file-size">(' + formatBytes(file.size) + ')</span>' +
                    '</div>' +
                    '<button class="download-btn" onclick="downloadFile(\'' + file.name + '\')">Download</button>';
                fileList.appendChild(fileItem);
            });
        }
        
        function formatBytes(bytes) {
            if (bytes < 1024) return bytes + ' B';
            if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
            return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
        }
        
        function setCanSpeed() {
            const speed = document.getElementById('can-speed').value;
            const speedName = speedNames[speed];
            
            ws.send(JSON.stringify({cmd: 'setSpeed', speed: parseInt(speed)}));
            
            const statusSpan = document.getElementById('speed-status');
            if (statusSpan) {
                statusSpan.textContent = '✓ Applied: ' + speedName;
                statusSpan.style.color = '#11998e';
                
                setTimeout(() => {
                    statusSpan.textContent = '';
                }, 3000);
            }
            
            console.log('CAN speed set to:', speedName);
        }
        
        function startLogging() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.send(JSON.stringify({cmd: 'startLogging'}));
                console.log('Start logging command sent');
            }
        }
        
        function stopLogging() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.send(JSON.stringify({cmd: 'stopLogging'}));
                console.log('Stop logging command sent');
            }
        }
        
        function refreshFiles() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.send(JSON.stringify({cmd: 'getFiles'}));
            }
        }
        
        function clearMessages() {
            canMessages = {};
            messageOrder = [];
            lastMessageData = {};
            document.getElementById('can-messages').innerHTML = '';
        }
        
        function downloadFile(filename) {
            window.location.href = '/download?file=' + encodeURIComponent(filename);
        }
        
        initWebSocket();
        setTimeout(() => { refreshFiles(); }, 2000);
    </script>
</body>
</html>
)rawliteral";

#endif