← back to Port Viewer

public/index.html

2985 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="icon" type="image/svg+xml" href="/favicon.svg">

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Port Viewer - Active Ports</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: 20px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
        }

        header {
            text-align: center;
            color: white;
            margin-bottom: 40px;
            animation: fadeIn 0.6s ease-in;
        }

        h1 {
            font-size: 3em;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }

        .subtitle {
            font-size: 1.2em;
            opacity: 0.9;
        }

        .stats {
            display: flex;
            justify-content: center;
            gap: 30px;
            margin: 20px 0;
            flex-wrap: wrap;
        }

        .stat-box {
            background: rgba(255,255,255,0.2);
            backdrop-filter: blur(10px);
            padding: 15px 30px;
            border-radius: 15px;
            color: white;
            font-weight: bold;
        }

        .stat-number {
            font-size: 2em;
            display: block;
        }

        .cards-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
            gap: 25px;
            animation: fadeIn 0.8s ease-in;
        }

        .card {
            background: white;
            border-radius: 20px;
            padding: 25px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            transition: transform 0.3s ease, box-shadow 0.3s ease;
            position: relative;
            overflow: hidden;
            animation: slideUp 0.5s ease-out;
        }

        .card::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 5px;
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
        }

        .card.active-user-app {
            border: 3px solid #00b894;
            box-shadow: 0 10px 30px rgba(0, 184, 148, 0.3);
        }

        .card.active-user-app::before {
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            height: 8px;
        }

        .card.offline-app {
            border: 3px solid #d63031;
            background: #ffe5e5;
            box-shadow: 0 10px 30px rgba(214, 48, 49, 0.3);
        }

        .card.offline-app::before {
            background: linear-gradient(90deg, #d63031 0%, #c0392b 100%);
            height: 8px;
        }

        .card.loading {
            border: 3px solid #0984e3;
            background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
            box-shadow: 0 10px 30px rgba(9, 132, 227, 0.3);
            position: relative;
        }

        .loading-spinner {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: #fff;
            animation: spin 0.8s linear infinite;
            margin-left: 8px;
            vertical-align: middle;
        }

        .loading-overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(9, 132, 227, 0.15);
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
            gap: 15px;
            border-radius: 20px;
            z-index: 10;
        }

        .loading-overlay .big-spinner {
            width: 60px;
            height: 60px;
            border: 5px solid rgba(9, 132, 227, 0.2);
            border-radius: 50%;
            border-top-color: #0984e3;
            animation: spin 1s linear infinite;
        }

        .loading-text {
            color: #0984e3;
            font-weight: 600;
            font-size: 1.2em;
            text-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        .card:hover {
            transform: translateY(-10px);
            box-shadow: 0 15px 40px rgba(0,0,0,0.3);
        }

        .port-number {
            font-size: 3em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 10px;
        }

        .port-info {
            margin: 10px 0;
        }

        .info-label {
            font-size: 0.9em;
            color: #666;
            text-transform: uppercase;
            letter-spacing: 1px;
            margin-bottom: 5px;
        }

        .info-value {
            font-size: 1.1em;
            color: #333;
            font-weight: 600;
            word-break: break-all;
        }

        .protocol-badge {
            display: inline-block;
            padding: 5px 15px;
            border-radius: 20px;
            font-size: 0.9em;
            font-weight: bold;
            margin-top: 10px;
        }

        .protocol-tcp {
            background: #e3f2fd;
            color: #1976d2;
        }

        .protocol-tcp6 {
            background: #f3e5f5;
            color: #7b1fa2;
        }

        .protocol-udp {
            background: #e8f5e9;
            color: #388e3c;
        }

        .loading {
            text-align: center;
            color: white;
            font-size: 1.5em;
            margin-top: 50px;
        }

        .spinner {
            border: 4px solid rgba(255,255,255,0.3);
            border-top: 4px solid white;
            border-radius: 50%;
            width: 50px;
            height: 50px;
            animation: spin 1s linear infinite;
            margin: 20px auto;
        }

        .refresh-btn {
            position: fixed;
            bottom: 30px;
            right: 30px;
            background: white;
            color: #667eea;
            border: none;
            padding: 15px 30px;
            border-radius: 50px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 5px 20px rgba(0,0,0,0.3);
            transition: all 0.3s ease;
        }

        .refresh-btn:hover {
            transform: scale(1.1);
            box-shadow: 0 8px 30px rgba(0,0,0,0.4);
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        @keyframes slideUp {
            from {
                opacity: 0;
                transform: translateY(30px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .error {
            background: rgba(255,255,255,0.9);
            color: #d32f2f;
            padding: 20px;
            border-radius: 15px;
            text-align: center;
            margin: 20px;
        }

        .app-name {
            font-size: 1.3em;
            font-weight: bold;
            color: #764ba2;
            margin: 15px 0;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .website-links {
            margin: 10px 0;
        }

        .website-link {
            display: inline-block;
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 8px 16px;
            border-radius: 20px;
            text-decoration: none;
            font-size: 0.9em;
            margin: 5px 5px 5px 0;
            transition: all 0.3s ease;
            position: relative;
        }

        .website-link:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }

        .web-view-container {
            margin-top: 15px;
            border-top: 2px solid #e0e0e0;
            padding-top: 15px;
        }

        .web-view-toggle {
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 20px;
            cursor: pointer;
            font-weight: bold;
            margin-bottom: 10px;
            width: 100%;
            transition: all 0.3s ease;
            font-size: 0.9em;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }

        .web-view-toggle:hover {
            transform: scale(1.02);
            box-shadow: 0 5px 15px rgba(0, 184, 148, 0.4);
        }

        .web-view-toggle.active {
            background: linear-gradient(90deg, #ff6b6b 0%, #ee5a6f 100%);
        }

        .web-view-iframe-container {
            display: block;
            width: 100%;
            height: 300px;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
            margin-top: 10px;
            position: relative;
            background: white;
            animation: slideDown 0.3s ease-out;
        }

        .web-view-iframe-container.active {
            display: block;
        }

        @keyframes slideDown {
            from {
                opacity: 0;
                max-height: 0;
            }
            to {
                opacity: 1;
                max-height: 400px;
            }
        }

        .web-view-iframe {
            width: 100%;
            height: 100%;
            border: none;
            background: white;
        }

        .web-view-loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 10px;
            color: #667eea;
        }

        .web-view-error {
            padding: 20px;
            text-align: center;
            color: #d63031;
            font-weight: bold;
        }

        .fullscreen-btn {
            position: absolute;
            top: 10px;
            right: 10px;
            background: rgba(102, 126, 234, 0.9);
            color: white;
            border: none;
            padding: 8px 12px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.85em;
            font-weight: bold;
            z-index: 10;
            transition: all 0.3s ease;
        }

        .fullscreen-btn:hover {
            background: rgba(102, 126, 234, 1);
            transform: scale(1.05);
        }

        .preview-container {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 80%;
            max-width: 1200px;
            height: 80%;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.5);
            z-index: 1000;
            display: none;
            flex-direction: column;
            overflow: hidden;
        }

        .preview-container.active {
            display: flex;
        }

        .preview-header {
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .preview-title {
            font-size: 1.5em;
            font-weight: bold;
        }

        .close-preview {
            background: rgba(255,255,255,0.3);
            border: none;
            color: white;
            font-size: 1.5em;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .close-preview:hover {
            background: rgba(255,255,255,0.5);
            transform: rotate(90deg);
        }

        .preview-iframe {
            flex: 1;
            border: none;
            background: white;
        }

        .preview-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.7);
            z-index: 999;
            display: none;
        }

        .preview-overlay.active {
            display: block;
        }

        .docker-badge {
            background: #e3f2fd;
            color: #1976d2;
            padding: 5px 12px;
            border-radius: 15px;
            font-size: 0.8em;
            display: inline-block;
            margin-top: 5px;
        }

        .open-icon {
            font-size: 0.8em;
            opacity: 0.8;
        }

        .directory-info {
            background: #f5f5f5;
            padding: 10px;
            border-radius: 10px;
            margin: 10px 0;
            font-size: 0.9em;
            color: #666;
            font-family: monospace;
            word-break: break-all;
        }

        .restart-btn {
            background: linear-gradient(90deg, #ff6b6b 0%, #ee5a6f 100%);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 20px;
            cursor: pointer;
            font-weight: bold;
            margin-top: 10px;
            width: 100%;
            transition: all 0.3s ease;
            font-size: 0.9em;
        }

        .restart-btn:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(238, 90, 111, 0.4);
        }

        .restart-btn:active {
            transform: scale(0.95);
        }

        .restart-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
        }

        .system-badge {
            background: #ffeaa7;
            color: #d63031;
            padding: 5px 12px;
            border-radius: 15px;
            font-size: 0.8em;
            display: inline-block;
            margin-top: 5px;
        }

        .user-app-indicator {
            background: #55efc4;
            color: #00b894;
            padding: 5px 12px;
            border-radius: 15px;
            font-size: 0.8em;
            display: inline-block;
            margin-top: 5px;
        }

        .last-port-info {
            background: #dfe6e9;
            padding: 8px 12px;
            border-radius: 10px;
            margin: 5px 0;
            font-size: 0.85em;
            color: #2d3436;
        }

        .offline-badge {
            background: #d63031;
            color: white;
            padding: 8px 15px;
            border-radius: 15px;
            font-size: 0.9em;
            display: inline-block;
            margin: 10px 0;
            font-weight: bold;
        }

        .offline-app .port-number {
            color: #d63031;
            opacity: 0.7;
        }

        .offline-app .info-value,
        .offline-app .info-label {
            opacity: 0.6;
        }

        .active-indicator {
            display: inline-block;
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #00b894;
            margin-left: 10px;
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }

        .add-dir-btn {
            position: fixed;
            bottom: 100px;
            right: 30px;
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            color: white;
            border: none;
            padding: 15px 30px;
            border-radius: 50px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 5px 20px rgba(0, 184, 148, 0.4);
            transition: all 0.3s ease;
        }

        .add-dir-btn:hover {
            transform: scale(1.1);
            box-shadow: 0 8px 30px rgba(0, 184, 148, 0.5);
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.7);
            z-index: 2000;
            justify-content: center;
            align-items: center;
        }

        .modal.active {
            display: flex;
        }

        .modal-content {
            background: white;
            border-radius: 20px;
            padding: 40px;
            max-width: 500px;
            width: 90%;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
            animation: slideUp 0.3s ease-out;
        }

        .modal-header {
            font-size: 2em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 20px;
        }

        .form-group {
            margin-bottom: 20px;
        }

        .form-label {
            display: block;
            font-weight: bold;
            color: #333;
            margin-bottom: 8px;
            font-size: 0.95em;
        }

        .form-input {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 10px;
            font-size: 1em;
            transition: border-color 0.3s ease;
        }

        .form-input:focus {
            outline: none;
            border-color: #667eea;
        }

        .form-buttons {
            display: flex;
            gap: 10px;
            margin-top: 30px;
        }

        .btn-submit {
            flex: 1;
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 15px;
            border-radius: 10px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .btn-submit:hover {
            transform: scale(1.05);
        }

        .btn-cancel {
            flex: 1;
            background: #ddd;
            color: #333;
            border: none;
            padding: 15px;
            border-radius: 10px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .btn-cancel:hover {
            background: #ccc;
        }

        .ask-server-section {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            padding: 30px;
            margin-bottom: 40px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
        }

        .ask-header {
            font-size: 1.8em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 10px;
        }

        .ask-subtitle {
            color: #666;
            margin-bottom: 20px;
            font-size: 0.95em;
        }

        .ask-input-container {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        .ask-input {
            flex: 1;
            padding: 15px 20px;
            border: 2px solid #ddd;
            border-radius: 50px;
            font-size: 1em;
            transition: border-color 0.3s ease;
        }

        .ask-input:focus {
            outline: none;
            border-color: #667eea;
        }

        .ask-btn {
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 15px 30px;
            border-radius: 50px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            white-space: nowrap;
        }

        .ask-btn:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }

        .ask-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
        }

        .ask-response {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 20px;
            margin-top: 20px;
            display: none;
            animation: slideUp 0.3s ease-out;
        }

        .ask-response.show {
            display: block;
        }

        .response-header {
            font-weight: bold;
            color: #667eea;
            margin-bottom: 10px;
            font-size: 1.1em;
        }

        .response-content {
            background: white;
            padding: 15px;
            border-radius: 10px;
            font-family: 'Courier New', monospace;
            font-size: 0.9em;
            white-space: pre-wrap;
            word-wrap: break-word;
            max-height: 400px;
            overflow-y: auto;
            color: #333;
        }

        .response-error {
            background: #ffe5e5;
            color: #d63031;
            padding: 15px;
            border-radius: 10px;
            font-weight: bold;
        }

        .quick-queries {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            margin-top: 15px;
        }

        .quick-query-btn {
            background: rgba(102, 126, 234, 0.1);
            color: #667eea;
            border: 2px solid #667eea;
            padding: 8px 16px;
            border-radius: 20px;
            font-size: 0.85em;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .quick-query-btn:hover {
            background: #667eea;
            color: white;
        }

        .directories-section {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            padding: 30px;
            margin-bottom: 40px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
        }

        .directories-header {
            font-size: 1.8em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 10px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .dir-header-actions {
            display: flex;
            gap: 10px;
        }

        .dir-add-btn {
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 20px;
            font-size: 0.9em;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .dir-add-btn:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(0, 184, 148, 0.4);
        }

        .dir-scan-btn {
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 20px;
            font-size: 0.9em;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .dir-scan-btn:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }

        .directories-list {
            margin-top: 20px;
        }

        .dir-item {
            background: white;
            border-radius: 15px;
            padding: 20px;
            margin-bottom: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: all 0.3s ease;
            border: 2px solid #e0e0e0;
        }

        .dir-item:hover {
            transform: translateY(-3px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
        }

        .dir-item.active {
            border-color: #00b894;
            background: linear-gradient(to right, rgba(0, 184, 148, 0.05), white);
        }

        .dir-item.offline {
            border-color: #d63031;
            background: linear-gradient(to right, rgba(214, 48, 49, 0.05), white);
        }

        .dir-info {
            flex: 1;
        }

        .dir-path {
            font-family: 'Courier New', monospace;
            font-weight: bold;
            color: #333;
            font-size: 1.1em;
            margin-bottom: 5px;
        }

        .dir-meta {
            display: flex;
            gap: 15px;
            font-size: 0.85em;
            color: #666;
            margin-bottom: 10px;
        }

        .dir-iframe-container {
            width: 100%;
            height: 250px;
            border-radius: 8px;
            overflow: hidden;
            background: white;
            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
            margin-top: 10px;
        }

        .dir-iframe {
            width: 100%;
            height: 100%;
            border: none;
        }

        .dir-status {
            display: inline-flex;
            align-items: center;
            gap: 5px;
            font-weight: bold;
        }

        .dir-status.active {
            color: #00b894;
        }

        .dir-status.offline {
            color: #d63031;
        }

        .dir-actions {
            display: flex;
            gap: 10px;
        }

        .dir-action-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 10px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            font-size: 0.85em;
        }

        .dir-action-btn.start {
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            color: white;
        }

        .dir-action-btn.restart {
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
        }

        .dir-action-btn.delete {
            background: #ff6b6b;
            color: white;
        }

        .dir-action-btn.stop {
            background: #e74c3c;
            color: white;
        }

        .dir-action-btn.assign {
            background: #f39c12;
            color: white;
        }

        .dir-action-btn.open {
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            color: white;
        }

        .dir-action-btn:hover {
            transform: scale(1.05);
        }

        .dir-action-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
        }

        .empty-directories {
            text-align: center;
            padding: 40px;
            color: #999;
        }

        .status-dot {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            animation: pulse 2s infinite;
        }

        .status-dot.active {
            background: #00b894;
        }

        .status-dot.offline {
            background: #d63031;
            animation: none;
        }

        .email-settings-section {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            padding: 30px;
            margin-bottom: 40px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
        }

        .email-header {
            font-size: 1.8em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 10px;
        }

        .email-content {
            display: flex;
            gap: 20px;
            align-items: center;
            flex-wrap: wrap;
        }

        .email-toggle {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .toggle-switch {
            position: relative;
            width: 60px;
            height: 30px;
            background: #ccc;
            border-radius: 30px;
            cursor: pointer;
            transition: background 0.3s;
        }

        .toggle-switch.active {
            background: #00b894;
        }

        .toggle-switch::after {
            content: '';
            position: absolute;
            width: 24px;
            height: 24px;
            background: white;
            border-radius: 50%;
            top: 3px;
            left: 3px;
            transition: left 0.3s;
        }

        .toggle-switch.active::after {
            left: 33px;
        }

        .email-input-group {
            flex: 1;
            min-width: 300px;
        }

        .email-input-group label {
            display: block;
            font-weight: bold;
            color: #333;
            margin-bottom: 5px;
            font-size: 0.9em;
        }

        .email-input-group input {
            width: 100%;
            padding: 10px 15px;
            border: 2px solid #ddd;
            border-radius: 10px;
            font-size: 1em;
            transition: border-color 0.3s;
        }

        .email-input-group input:focus {
            outline: none;
            border-color: #667eea;
        }

        .save-email-btn {
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 10px 25px;
            border-radius: 10px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .save-email-btn:hover {
            transform: scale(1.05);
        }

        .email-status {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 10px 20px;
            border-radius: 10px;
            background: #e8f5e9;
            color: #2e7d32;
            font-weight: bold;
            font-size: 0.9em;
        }

        .email-status.disabled {
            background: #ffebee;
            color: #c62828;
        }

        .login-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            z-index: 10000;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .login-overlay.hidden {
            display: none;
        }

        .login-box {
            background: white;
            border-radius: 20px;
            padding: 50px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
            text-align: center;
            max-width: 400px;
            width: 90%;
            animation: slideUp 0.5s ease-out;
        }

        .login-logo {
            font-size: 4em;
            margin-bottom: 20px;
        }

        .login-title {
            font-size: 2em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 10px;
        }

        .login-subtitle {
            color: #666;
            margin-bottom: 30px;
        }

        .login-input {
            width: 100%;
            padding: 15px;
            border: 2px solid #ddd;
            border-radius: 10px;
            font-size: 1em;
            margin-bottom: 20px;
            transition: border-color 0.3s;
        }

        .login-input:focus {
            outline: none;
            border-color: #667eea;
        }

        .login-btn {
            width: 100%;
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 15px;
            border-radius: 10px;
            font-size: 1.1em;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .login-btn:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }

        .login-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
        }

        .login-error {
            background: #ffebee;
            color: #c62828;
            padding: 10px;
            border-radius: 10px;
            margin-bottom: 20px;
            display: none;
        }

        .login-error.show {
            display: block;
        }

        .logout-btn {
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(255, 255, 255, 0.2);
            color: white;
            border: 2px solid white;
            padding: 10px 20px;
            border-radius: 20px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            z-index: 1000;
        }

        .logout-btn:hover {
            background: white;
            color: #667eea;
        }

        .live-domains-section {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border-radius: 20px;
            padding: 30px;
            margin-bottom: 40px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            color: white;
        }

        .domains-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
            gap: 20px;
            margin-top: 20px;
        }

        .domain-card {
            background: rgba(255, 255, 255, 0.15);
            backdrop-filter: blur(10px);
            border-radius: 12px;
            padding: 20px;
            border: 2px solid rgba(255, 255, 255, 0.2);
            transition: all 0.3s ease;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .domain-card:hover {
            transform: translateY(-5px);
            background: rgba(255, 255, 255, 0.25);
            border-color: rgba(255, 255, 255, 0.4);
        }

        .domain-card.live {
            border-color: #00b894;
            background: rgba(0, 184, 148, 0.2);
        }

        .domain-status {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #00b894;
            box-shadow: 0 0 10px #00b894;
            animation: pulse 2s infinite;
        }

        .domain-name {
            flex: 1;
            font-weight: 600;
            font-size: 1.1em;
        }

        .domain-link {
            color: white;
            text-decoration: none;
            opacity: 0.8;
            transition: opacity 0.3s;
        }

        .domain-link:hover {
            opacity: 1;
        }

        .domain-iframe-container {
            width: 100%;
            height: 300px;
            border-radius: 8px;
            overflow: hidden;
            background: white;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
        }

        .domain-iframe {
            width: 100%;
            height: 100%;
            border: none;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }

        .all-dirs-section {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            padding: 30px;
            margin-bottom: 40px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
        }

        .all-dirs-header {
            font-size: 1.8em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .all-dirs-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
            gap: 15px;
            margin-top: 20px;
        }

        .all-dir-card {
            background: white;
            border: 2px solid #e0e0e0;
            border-radius: 12px;
            padding: 15px;
            transition: all 0.3s ease;
            cursor: pointer;
        }

        .all-dir-card:hover {
            transform: translateY(-3px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
            border-color: #667eea;
        }

        .all-dir-card.tracked {
            background: linear-gradient(to right, rgba(0, 184, 148, 0.05), white);
            border-color: #00b894;
        }

        .all-dir-name {
            font-family: 'Courier New', monospace;
            font-weight: bold;
            color: #333;
            font-size: 0.95em;
            margin-bottom: 10px;
            word-break: break-all;
        }

        .all-dir-path {
            font-family: 'Courier New', monospace;
            font-size: 0.75em;
            color: #999;
            margin-bottom: 10px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .all-dir-status {
            display: inline-block;
            padding: 4px 10px;
            border-radius: 12px;
            font-size: 0.75em;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .all-dir-status.tracked {
            background: #e8f5e9;
            color: #2e7d32;
        }

        .all-dir-status.untracked {
            background: #fff3cd;
            color: #856404;
        }

        .all-dir-actions {
            display: flex;
            gap: 5px;
        }

        .all-dir-btn {
            flex: 1;
            padding: 8px 12px;
            border: none;
            border-radius: 8px;
            font-weight: bold;
            cursor: pointer;
            font-size: 0.8em;
            transition: all 0.3s ease;
        }

        .all-dir-btn:hover {
            transform: scale(1.05);
        }

        .all-dir-btn.add {
            background: linear-gradient(90deg, #00b894 0%, #00a085 100%);
            color: white;
        }

        .all-dir-btn.tracked {
            background: #e0e0e0;
            color: #666;
            cursor: default;
        }

        .loading-dirs {
            text-align: center;
            padding: 40px;
            color: #999;
        }
    </style>
</head>
<body>
    <!-- Login Overlay -->
    <div class="login-overlay" id="loginOverlay">
        <div class="login-box">
            <div class="login-logo">🔒</div>
            <div class="login-title">Port Viewer</div>
            <div class="login-subtitle">Enter password to access</div>
            <div class="login-error" id="loginError">Invalid password</div>
            <form onsubmit="handleLogin(event)">
                <input
                    type="password"
                    class="login-input"
                    id="loginPassword"
                    placeholder="Password"
                    autocomplete="current-password"
                    required
                />
                <button type="submit" class="login-btn" id="loginBtn">
                    🔓 Login
                </button>
            </form>
        </div>
    </div>

    <button class="logout-btn" id="logoutBtn" onclick="handleLogout()" style="display: none;">
        🚪 Logout
    </button>

    <div class="container">
        <header>
            <h1>🚀 Port Viewer</h1>
            <p class="subtitle">Active Network Ports & Services</p>
            <div class="stats" id="stats"></div>
        </header>

        <!-- Live Domains Section -->
        <div class="live-domains-section">
            <div class="all-dirs-header">
                <span>🌐 Live Domains</span>
                <button class="dir-scan-btn" onclick="loadDomains()">🔄 Refresh</button>
            </div>
            <div id="domainsContainer">
                <div class="loading-dirs">
                    <div class="spinner"></div>
                    <p>Loading domains...</p>
                </div>
            </div>
        </div>

        <!-- All Server Directories Section -->
        <div class="all-dirs-section">
            <div class="all-dirs-header">
                <span>📂 All Server Directories</span>
                <button class="dir-scan-btn" onclick="loadAllServerDirectories()">🔄 Refresh</button>
            </div>
            <div id="allDirsContainer">
                <div class="loading-dirs">
                    <div class="spinner"></div>
                    <p>Scanning server directories...</p>
                </div>
            </div>
        </div>

        <!-- Email Alert Settings -->
        <div class="email-settings-section">
            <div class="email-header">📧 Email Alerts</div>
            <div class="email-content">
                <div class="email-toggle">
                    <span style="font-weight: bold;">Alerts:</span>
                    <div class="toggle-switch" id="emailToggle" onclick="toggleEmailAlerts()"></div>
                </div>
                <div class="email-input-group">
                    <label>Email Address</label>
                    <input type="email" id="emailRecipient" placeholder="your@email.com" />
                </div>
                <button class="save-email-btn" onclick="saveEmailConfig()">💾 Save Settings</button>
                <div class="email-status" id="emailStatus">
                    <span>⏱️ Monitoring every 60 seconds</span>
                </div>
            </div>
        </div>

        <!-- Server Directories Section -->
        <div class="directories-section">
            <div class="directories-header">
                <span>📁 Server Directories</span>
                <div class="dir-header-actions">
                    <button class="dir-scan-btn" onclick="scanAllDirectories()">🔍 Scan All</button>
                    <button class="dir-add-btn" onclick="openAddDirectoryModal()">➕ Add Directory</button>
                </div>
            </div>
            <div class="directories-list" id="directoriesList">
                <div class="empty-directories">
                    Loading directories...
                </div>
            </div>
        </div>

        <!-- Ask the Server Section -->
        <div class="ask-server-section">
            <div class="ask-header">💬 Ask the Server</div>
            <div class="ask-subtitle">
                👋 Ask me anything about this server! Try: "What's the uptime?", "Show memory usage", "How many ports are open?", or type "help" for more commands.
            </div>
            <div class="ask-input-container">
                <input
                    type="text"
                    class="ask-input"
                    id="askInput"
                    placeholder="Ask about uptime, memory, CPU, disk, ports, processes..."
                    onkeypress="if(event.key === 'Enter') askServer()"
                />
                <button class="ask-btn" id="askBtn" onclick="askServer()">Send 🚀</button>
            </div>
            <div class="quick-queries">
                <button class="quick-query-btn" onclick="quickQuery('uptime')">⏱️ Uptime</button>
                <button class="quick-query-btn" onclick="quickQuery('memory')">💾 Memory</button>
                <button class="quick-query-btn" onclick="quickQuery('cpu')">🖥️ CPU</button>
                <button class="quick-query-btn" onclick="quickQuery('disk')">💿 Disk</button>
                <button class="quick-query-btn" onclick="quickQuery('ports')">🔌 Ports</button>
                <button class="quick-query-btn" onclick="quickQuery('processes')">⚙️ Processes</button>
                <button class="quick-query-btn" onclick="quickQuery('help')">❓ Help</button>
            </div>
            <div class="ask-response" id="askResponse"></div>
        </div>

        <div id="loading" class="loading">
            <div class="spinner"></div>
            <p>Loading ports...</p>
        </div>

        <div id="error" style="display: none;"></div>
        <div class="cards-grid" id="cards"></div>
    </div>

    <button class="refresh-btn" onclick="loadPorts()">🔄 Refresh</button>

    <!-- Add Directory Modal -->
    <div class="modal" id="addDirModal">
        <div class="modal-content">
            <div class="modal-header">📁 Add Directory</div>
            <form id="addDirForm" onsubmit="handleAddDirectory(event)">
                <div class="form-group">
                    <label class="form-label">Directory Path *</label>
                    <input type="text" class="form-input" id="dirPath" placeholder="/root/my-app" required>
                </div>
                <div class="form-group">
                    <label class="form-label">Port Number (optional)</label>
                    <input type="number" class="form-input" id="dirPort" placeholder="3000">
                </div>
                <div class="form-group">
                    <label class="form-label">App Name (optional)</label>
                    <input type="text" class="form-input" id="dirAppName" placeholder="My App">
                </div>
                <div class="form-buttons">
                    <button type="button" class="btn-cancel" onclick="closeAddDirectoryModal()">Cancel</button>
                    <button type="submit" class="btn-submit">Add Directory</button>
                </div>
            </form>
        </div>
    </div>

    <!-- Assign Port Modal -->
    <div class="modal" id="assignPortModal">
        <div class="modal-content">
            <div class="modal-header">🔢 Assign Port</div>
            <form id="assignPortForm" onsubmit="handleAssignPort(event)">
                <div class="form-group">
                    <label class="form-label">Directory</label>
                    <input type="text" class="form-input" id="assignDirPath" readonly>
                </div>
                <div class="form-group">
                    <label class="form-label">Port Number *</label>
                    <input type="number" class="form-input" id="assignPort" placeholder="3000" required>
                </div>
                <div class="form-group">
                    <label class="form-label">App Name (optional)</label>
                    <input type="text" class="form-input" id="assignAppName" placeholder="My App">
                </div>
                <div class="form-buttons">
                    <button type="button" class="btn-cancel" onclick="closeAssignPortModal()">Cancel</button>
                    <button type="submit" class="btn-submit">Assign Port</button>
                </div>
            </form>
        </div>
    </div>

    <!-- Scan Directories Modal -->
    <div class="modal" id="scanDirModal">
        <div class="modal-content" style="max-width: 700px;">
            <div class="modal-header">🔍 All Server Directories</div>
            <div id="scanDirList" style="max-height: 400px; overflow-y: auto; margin: 20px 0;">
                <div style="text-align: center; padding: 20px;">
                    <div class="spinner"></div>
                    <p>Scanning directories...</p>
                </div>
            </div>
            <div class="form-buttons">
                <button type="button" class="btn-cancel" onclick="closeScanDirModal()">Close</button>
            </div>
        </div>
    </div>

    <div class="preview-overlay" id="previewOverlay" onclick="closePreview()"></div>
    <div class="preview-container" id="previewContainer">
        <div class="preview-header">
            <div class="preview-title" id="previewTitle"></div>
            <button class="close-preview" onclick="closePreview()">×</button>
        </div>
        <iframe class="preview-iframe" id="previewIframe"></iframe>
    </div>

    <script>
        // Authentication Management
        let authToken = localStorage.getItem('authToken');
        let serverInfo = { serverIp: 'localhost', port: 6000 }; // Default values

        // Fetch server info on page load
        fetch('/api/server-info')
            .then(response => response.json())
            .then(info => {
                serverInfo = info;
            })
            .catch(err => console.error('Failed to fetch server info:', err));

        // Check if authenticated on page load
        if (!authToken) {
            document.getElementById('loginOverlay').classList.remove('hidden');
        } else {
            document.getElementById('loginOverlay').classList.add('hidden');
            document.getElementById('logoutBtn').style.display = 'block';
            initializeApp();
        }

        async function handleLogin(event) {
            event.preventDefault();

            const password = document.getElementById('loginPassword').value;
            const btn = document.getElementById('loginBtn');
            const errorDiv = document.getElementById('loginError');

            btn.disabled = true;
            btn.textContent = '🔄 Logging in...';
            errorDiv.classList.remove('show');

            try {
                const response = await fetch('/api/login', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ password })
                });

                const result = await response.json();

                if (result.success && result.token) {
                    authToken = result.token;
                    localStorage.setItem('authToken', authToken);

                    document.getElementById('loginOverlay').classList.add('hidden');
                    document.getElementById('logoutBtn').style.display = 'block';
                    document.getElementById('loginPassword').value = '';

                    initializeApp();
                } else {
                    errorDiv.classList.add('show');
                    btn.disabled = false;
                    btn.textContent = '🔓 Login';
                }
            } catch (err) {
                errorDiv.textContent = 'Connection error. Please try again.';
                errorDiv.classList.add('show');
                btn.disabled = false;
                btn.textContent = '🔓 Login';
            }
        }

        async function handleLogout() {
            if (!confirm('Are you sure you want to logout?')) return;

            try {
                await fetch('/api/logout', {
                    method: 'POST',
                    headers: {
                        'Authorization': `Bearer ${authToken}`
                    }
                });
            } catch (err) {
                console.error('Logout error:', err);
            }

            authToken = null;
            localStorage.removeItem('authToken');
            document.getElementById('loginOverlay').classList.remove('hidden');
            document.getElementById('logoutBtn').style.display = 'none';
        }

        // Wrapper for authenticated fetch
        async function authFetch(url, options = {}) {
            if (!options.headers) options.headers = {};
            options.headers['Authorization'] = `Bearer ${authToken}`;

            const response = await fetch(url, options);

            if (response.status === 401) {
                // Session expired
                authToken = null;
                localStorage.removeItem('authToken');
                document.getElementById('loginOverlay').classList.remove('hidden');
                document.getElementById('logoutBtn').style.display = 'none';
                throw new Error('Session expired. Please login again.');
            }

            return response;
        }

        function initializeApp() {
            loadPorts();
            loadDirectories();
            loadEmailConfig();
            loadDomains();
            loadAllServerDirectories();

            // Auto-refresh every 2 minutes
            setInterval(() => {
                loadPorts();
                loadDirectories();
                loadDomains();
            }, 120000);
        }

        // Load live domains
        async function loadDomains() {
            const container = document.getElementById('domainsContainer');
            container.innerHTML = `
                <div class="loading-dirs">
                    <div class="spinner"></div>
                    <p>Loading domains...</p>
                </div>
            `;

            try {
                const response = await authFetch('/api/domains');
                const data = await response.json();

                if (data.domains && data.domains.length > 0) {
                    const grid = document.createElement('div');
                    grid.className = 'domains-container';

                    data.domains.forEach(domain => {
                        const card = document.createElement('div');
                        card.className = 'domain-card live';

                        const proxyUrl = `/api/proxy/${encodeURIComponent('https://' + domain)}`;

                        card.innerHTML = `
                            <div style="display: flex; align-items: center; gap: 12px;">
                                <div class="domain-status"></div>
                                <div class="domain-name">${domain}</div>
                                <a href="https://${domain}" target="_blank" rel="noopener noreferrer" class="domain-link">🔗</a>
                            </div>
                            <div class="domain-iframe-container">
                                <iframe class="domain-iframe" src="${proxyUrl}" sandbox="allow-same-origin allow-scripts allow-forms allow-popups"></iframe>
                            </div>
                        `;

                        grid.appendChild(card);
                    });

                    container.innerHTML = '';
                    container.appendChild(grid);
                } else {
                    container.innerHTML = `
                        <div class="loading-dirs">
                            🌐 No domains configured
                        </div>
                    `;
                }
            } catch (err) {
                container.innerHTML = `
                    <div class="loading-dirs" style="color: #fff3cd;">
                        ❌ Error: ${err.message}
                    </div>
                `;
            }
        }

        // Load all server directories
        async function loadAllServerDirectories() {
            const container = document.getElementById('allDirsContainer');
            container.innerHTML = `
                <div class="loading-dirs">
                    <div class="spinner"></div>
                    <p>Scanning server directories...</p>
                </div>
            `;

            try {
                // Fetch all directories
                const scanResponse = await authFetch('/api/scan-directories');
                const scanResult = await scanResponse.json();

                // Fetch tracked ports to know which are already tracked
                const portsResponse = await authFetch('/api/ports');
                const ports = await portsResponse.json();

                // Fetch directory data to get lastSeen timestamps
                const dirDataResponse = await authFetch('/api/directories');
                const directoryData = await dirDataResponse.json();

                const trackedDirs = new Set();
                ports.forEach(port => {
                    if (port.directory && port.isUserApp) {
                        trackedDirs.add(port.directory);
                    }
                });

                if (scanResult.directories && scanResult.directories.length > 0) {
                    // Sort directories by most recently accessed
                    const sortedDirs = scanResult.directories.sort((a, b) => {
                        const aData = directoryData[a];
                        const bData = directoryData[b];

                        // If both have lastSeen, sort by timestamp (most recent first)
                        if (aData?.lastSeen && bData?.lastSeen) {
                            return new Date(bData.lastSeen) - new Date(aData.lastSeen);
                        }
                        // Tracked directories come before untracked
                        if (aData?.lastSeen) return -1;
                        if (bData?.lastSeen) return 1;
                        // Both untracked - maintain alphabetical order
                        return a.localeCompare(b);
                    });

                    const grid = document.createElement('div');
                    grid.className = 'all-dirs-grid';

                    sortedDirs.forEach(dir => {
                        const isTracked = trackedDirs.has(dir);
                        const dirName = dir.split('/').pop();
                        const dirData = directoryData[dir];
                        const lastPort = dirData?.lastPort || 'N/A';

                        const card = document.createElement('div');
                        card.className = `all-dir-card ${isTracked ? 'tracked' : ''}`;

                        card.innerHTML = `
                            <div class="all-dir-name">📁 ${dirName}</div>
                            <div class="all-dir-path" title="${dir}">${dir}</div>
                            <div class="all-dir-status ${isTracked ? 'tracked' : 'untracked'}">
                                ${isTracked ? '✅ Tracked' : '⚠️ Not Tracked'}
                            </div>
                            ${dirData?.lastPort ? `<div style="font-size: 0.75em; color: #666; margin: 5px 0;">Last Port: ${lastPort}</div>` : ''}
                            <div class="all-dir-actions">
                                ${isTracked
                                    ? '<button class="all-dir-btn tracked" disabled>Already Tracked</button>'
                                    : `<button class="all-dir-btn add" onclick="quickAddDirectory(event, '${dir}', '${dirName}')">➕ Add & Start</button>`
                                }
                            </div>
                        `;

                        grid.appendChild(card);
                    });

                    container.innerHTML = '';
                    container.appendChild(grid);
                } else {
                    container.innerHTML = `
                        <div class="loading-dirs">
                            📂 No Node.js projects found on the server
                        </div>
                    `;
                }
            } catch (err) {
                container.innerHTML = `
                    <div class="loading-dirs" style="color: #d63031;">
                        ❌ Error: ${err.message}
                    </div>
                `;
            }
        }

        async function quickAddDirectory(event, directory, appName) {
            // Fetch directory data to check if there's a last port
            const dirDataResponse = await authFetch('/api/directories');
            const directoryData = await dirDataResponse.json();
            const lastPort = directoryData[directory]?.lastPort;

            // Suggest an available port if no last port
            let suggestedPort = lastPort || '3000';
            if (!lastPort) {
                try {
                    const portResponse = await authFetch('/api/suggest-port');
                    const portData = await portResponse.json();
                    suggestedPort = portData.port;
                } catch (err) {
                    console.error('Failed to get suggested port:', err);
                }
            }

            const button = event.target;
            const card = button.closest('.all-dir-card');
            const originalButtonHTML = button.innerHTML;

            const port = prompt(`Enter port number for ${appName} (suggested: ${suggestedPort}):`, suggestedPort);
            if (!port) return;

            // Start loading spinner IMMEDIATELY after OK is clicked
            button.disabled = true;
            button.innerHTML = '⏳ Adding<span class="loading-spinner"></span>';

            card.classList.add('loading');
            const overlay = document.createElement('div');
            overlay.className = 'loading-overlay';
            overlay.innerHTML = `
                <div class="big-spinner"></div>
                <div class="loading-text">🚀 Adding & starting ${appName}...</div>
            `;
            card.appendChild(overlay);

            try {
                // Add the directory
                overlay.querySelector('.loading-text').textContent = `📝 Adding ${appName}...`;
                const addResponse = await authFetch('/api/add-directory', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        directory: directory,
                        port: port,
                        appName: appName
                    })
                });

                const addResult = await addResponse.json();

                if (addResult.success) {
                    // Now start it
                    overlay.querySelector('.loading-text').textContent = `🚀 Starting ${appName}...`;
                    button.innerHTML = '⏳ Starting<span class="loading-spinner"></span>';

                    const startResponse = await authFetch(`/api/restart/${encodeURIComponent(directory)}`);
                    const startResult = await startResponse.json();

                    if (startResult.success) {
                        overlay.querySelector('.loading-text').textContent = `✅ ${appName} started successfully!`;
                        button.innerHTML = '✅ Started!';
                        button.style.background = '#00b894';

                        // ALWAYS open the app
                        setTimeout(() => {
                            const url = `http://${serverInfo.serverIp}:${port}`;
                            window.open(url, '_blank');
                        }, 1500);

                        // Reload everything
                        setTimeout(() => {
                            card.classList.remove('loading');
                            overlay.remove();
                            loadAllServerDirectories();
                            loadDirectories();
                            loadPorts();
                        }, 3000);
                    } else {
                        overlay.querySelector('.loading-text').textContent = `❌ Failed to start`;
                        button.innerHTML = '❌ Failed';
                        button.style.background = '#d63031';
                        setTimeout(() => {
                            card.classList.remove('loading');
                            overlay.remove();
                            button.innerHTML = originalButtonHTML;
                            button.style.background = '';
                            button.disabled = false;
                        }, 3000);
                        alert(`✅ ${appName} added but failed to start: ${startResult.message || startResult.error}`);
                    }
                } else {
                    card.classList.remove('loading');
                    overlay.remove();
                    button.innerHTML = originalButtonHTML;
                    button.disabled = false;
                    alert(`❌ Error: ${addResult.error}`);
                }
            } catch (err) {
                card.classList.remove('loading');
                if (overlay && overlay.parentNode) overlay.remove();
                button.innerHTML = originalButtonHTML;
                button.disabled = false;
                alert(`❌ Error: ${err.message}`);
            }
        }

        async function loadPorts() {
            const loading = document.getElementById('loading');
            const cards = document.getElementById('cards');
            const error = document.getElementById('error');
            const stats = document.getElementById('stats');

            loading.style.display = 'block';
            cards.innerHTML = '';
            error.style.display = 'none';

            try {
                const response = await authFetch('/api/ports');
                const ports = await response.json();

                loading.style.display = 'none';

                if (ports.error) {
                    error.innerHTML = `<div class="error">${ports.error}</div>`;
                    error.style.display = 'block';
                    return;
                }

                // Display stats
                const tcpCount = ports.filter(p => p.protocol.includes('TCP')).length;
                const udpCount = ports.filter(p => p.protocol.includes('UDP')).length;

                stats.innerHTML = `
                    <div class="stat-box">
                        <span class="stat-number">${ports.length}</span>
                        <div>Total Ports</div>
                    </div>
                    <div class="stat-box">
                        <span class="stat-number">${tcpCount}</span>
                        <div>TCP</div>
                    </div>
                    <div class="stat-box">
                        <span class="stat-number">${udpCount}</span>
                        <div>UDP</div>
                    </div>
                `;

                // Display cards
                ports.forEach((port, index) => {
                    const card = document.createElement('div');
                    card.className = 'card';
                    card.style.animationDelay = `${index * 0.05}s`;

                    // Add special classes for user apps
                    if (port.isUserApp && port.directory) {
                        if (port.isOffline) {
                            card.className += ' offline-app';
                        } else {
                            card.className += ' active-user-app';
                        }
                    }

                    const protocolClass = `protocol-${port.protocol.toLowerCase()}`;

                    let websiteLinksHTML = '';
                    let webViewHTML = '';
                    if (port.websites && port.websites.length > 0) {
                        websiteLinksHTML = '<div class="website-links">';
                        const firstWebsite = port.websites[0];
                        const firstUrl = firstWebsite.includes('localhost') || firstWebsite.includes('127.0.0.1')
                            ? `http://localhost:${port.port}`
                            : `https://${firstWebsite}`;

                        port.websites.forEach(website => {
                            const url = website.includes('localhost') || website.includes('127.0.0.1')
                                ? `http://localhost:${port.port}`
                                : `https://${website}`;
                            websiteLinksHTML += `
                                <a href="${url}" class="website-link" target="_blank" rel="noopener noreferrer"
                                   onmouseover="showPreview('${url}', '${website}')"
                                   onmouseout="hidePreviewDelayed()">
                                    ${website} <span class="open-icon">↗</span>
                                </a>
                            `;
                        });
                        websiteLinksHTML += '</div>';

                        // Add web view container for the first website - always visible
                        // Use direct URL for better CSS loading, fallback to proxy if needed
                        const directUrl = firstUrl;
                        const proxyUrl = `/api/proxy/${encodeURIComponent(firstUrl)}`;
                        webViewHTML = `
                            <div class="web-view-container">
                                <div class="web-view-iframe-container active" id="webview-${port.port}">
                                    <button class="fullscreen-btn" onclick="openFullscreen('${firstUrl}', '${firstWebsite}')">⛶ Fullscreen</button>
                                    <iframe class="web-view-iframe" id="iframe-${port.port}" src="${directUrl}" sandbox="allow-same-origin allow-scripts allow-forms allow-popups"></iframe>
                                </div>
                            </div>
                        `;
                    }

                    let dockerHTML = '';
                    if (port.docker) {
                        dockerHTML = `<div class="docker-badge">🐳 ${port.docker.image}</div>`;
                    }

                    let directoryHTML = '';
                    let restartButtonHTML = '';
                    let appTypeHTML = '';
                    let statusHTML = '';

                    if (port.isUserApp && port.directory) {
                        if (port.isOffline) {
                            appTypeHTML = '<div class="offline-badge">❌ OFFLINE</div>';
                            statusHTML = `<div class="last-port-info">Last Port: ${port.port}</div>`;
                            restartButtonHTML = `<button class="restart-btn" onclick="restartApp('${port.directory}', '${port.port}')">🔄 Start App</button>`;
                        } else {
                            appTypeHTML = '<span class="user-app-indicator">👤 User App <span class="active-indicator"></span></span>';
                            restartButtonHTML = `<button class="restart-btn" onclick="restartApp('${port.directory}', '${port.port}')">🔄 Restart App</button>`;
                        }
                        directoryHTML = `<div class="directory-info">📁 ${port.directory}</div>`;
                    } else if (!port.isUserApp && port.port) {
                        const sysPort = parseInt(port.port);
                        if ([22, 53, 80, 443, 111, 123, 514].includes(sysPort)) {
                            appTypeHTML = '<span class="system-badge">⚙️ System Service</span>';
                        }
                    }

                    let protocolBadge = port.protocol !== 'N/A' ? `<span class="protocol-badge ${protocolClass}">${port.protocol}</span>` : '';

                    card.innerHTML = `
                        <div class="port-number">:${port.port}</div>
                        ${port.appName ? `<div class="app-name">📱 ${port.appName}</div>` : ''}
                        ${appTypeHTML}
                        ${statusHTML}
                        ${websiteLinksHTML}
                        ${webViewHTML}
                        ${dockerHTML}
                        ${directoryHTML}
                        <div class="port-info">
                            <div class="info-label">Address</div>
                            <div class="info-value">${port.address}</div>
                        </div>
                        <div class="port-info">
                            <div class="info-label">Process</div>
                            <div class="info-value">${port.process}</div>
                        </div>
                        ${protocolBadge}
                        ${restartButtonHTML}
                    `;

                    cards.appendChild(card);
                });

                if (ports.length === 0) {
                    cards.innerHTML = '<div class="error">No listening ports found</div>';
                }
            } catch (err) {
                loading.style.display = 'none';
                error.innerHTML = `<div class="error">Failed to load ports: ${err.message}</div>`;
                error.style.display = 'block';
            }
        }


        // Email configuration functions
        async function loadEmailConfig() {
            try {
                const response = await authFetch('/api/email-config');
                const config = await response.json();

                document.getElementById('emailRecipient').value = config.recipient || '';
                const toggle = document.getElementById('emailToggle');
                const status = document.getElementById('emailStatus');

                if (config.enabled) {
                    toggle.classList.add('active');
                    status.classList.remove('disabled');
                    status.innerHTML = '<span>✅ Alerts Enabled - Monitoring every 60 seconds</span>';
                } else {
                    toggle.classList.remove('active');
                    status.classList.add('disabled');
                    status.innerHTML = '<span>❌ Alerts Disabled</span>';
                }
            } catch (err) {
                console.error('Failed to load email config:', err);
            }
        }

        function toggleEmailAlerts() {
            const toggle = document.getElementById('emailToggle');
            toggle.classList.toggle('active');
        }

        async function saveEmailConfig() {
            const enabled = document.getElementById('emailToggle').classList.contains('active');
            const recipient = document.getElementById('emailRecipient').value.trim();

            if (!recipient) {
                alert('Please enter an email address');
                return;
            }

            try {
                const response = await authFetch('/api/email-config', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ enabled, recipient })
                });

                const result = await response.json();

                if (result.success) {
                    alert('✅ Email settings saved successfully!');
                    loadEmailConfig();
                } else {
                    alert(`❌ Error: ${result.error}`);
                }
            } catch (err) {
                alert(`❌ Error: ${err.message}`);
            }
        }

        // Load and display directories
        async function loadDirectories() {
            try {
                const response = await authFetch('/api/ports');
                const ports = await response.json();

                // Extract unique directories and their status
                const directories = {};

                ports.forEach(port => {
                    if (port.directory && port.isUserApp) {
                        directories[port.directory] = {
                            path: port.directory,
                            appName: port.appName || path.basename(port.directory),
                            port: port.port,
                            isActive: !port.isOffline,
                            process: port.process,
                            lastSeen: new Date().toISOString()
                        };
                    }
                });

                displayDirectories(directories);
            } catch (err) {
                console.error('Failed to load directories:', err);
            }
        }

        function displayDirectories(directories) {
            const container = document.getElementById('directoriesList');
            const dirArray = Object.values(directories);

            if (dirArray.length === 0) {
                container.innerHTML = `
                    <div class="empty-directories">
                        📂 No directories tracked yet. Click "Add Directory" to add one!
                    </div>
                `;
                return;
            }

            container.innerHTML = dirArray.map(dir => {
                const statusClass = dir.isActive ? 'active' : 'offline';
                const statusText = dir.isActive ? 'ONLINE' : 'OFFLINE';

                let actionButtons = '';
                if (dir.isActive) {
                    actionButtons = `
                        <button class="dir-action-btn open" onclick="openApp('${dir.port}')">🌐 Open</button>
                        <button class="dir-action-btn restart" onclick="restartDirectory('${dir.path}', '${dir.port}')">🔄 Restart</button>
                        <button class="dir-action-btn stop" onclick="stopPort('${dir.port}')">⏹️ Stop</button>
                    `;
                } else {
                    actionButtons = `
                        <button class="dir-action-btn start" onclick="restartDirectory('${dir.path}', '${dir.port}')">▶️ Start</button>
                    `;
                }

                const iframeHTML = dir.isActive ? `
                    <div class="dir-iframe-container">
                        <iframe class="dir-iframe" src="/api/proxy/${encodeURIComponent('http://' + serverInfo.serverIp + ':' + dir.port)}" sandbox="allow-same-origin allow-scripts allow-forms allow-popups"></iframe>
                    </div>
                ` : '';

                return `
                    <div class="dir-item ${statusClass}">
                        <div class="dir-info">
                            <div class="dir-path">📁 ${dir.path}</div>
                            <div class="dir-meta">
                                <span class="dir-status ${statusClass}">
                                    <span class="status-dot ${statusClass}"></span>
                                    ${statusText}
                                </span>
                                <span>Port: ${dir.port}</span>
                                <span>App: ${dir.appName}</span>
                            </div>
                            ${iframeHTML}
                        </div>
                        <div class="dir-actions">
                            ${actionButtons}
                            <button class="dir-action-btn assign" onclick="openAssignPortModal('${dir.path}', '${dir.port}', '${dir.appName}')">🔢 Assign</button>
                            <button class="dir-action-btn delete" onclick="deleteDirectory('${dir.path}')">🗑️ Remove</button>
                        </div>
                    </div>
                `;
            }).join('');
        }

        async function deleteDirectory(directory) {
            if (!confirm(`Are you sure you want to remove ${directory} from tracking?\n\nThis won't delete the actual directory, just stop tracking it.`)) {
                return;
            }

            try {
                const response = await authFetch(`/api/delete-directory/${encodeURIComponent(directory)}`);
                const result = await response.json();

                if (result.success) {
                    loadDirectories();
                    loadPorts();
                } else {
                    alert(`❌ Error: ${result.error}`);
                }
            } catch (err) {
                alert(`❌ Error: ${err.message}`);
            }
        }

        async function restartDirectory(directory, port) {
            if (!confirm(`Are you sure you want to restart the app in ${directory}?`)) {
                return;
            }

            const button = event.target;
            const originalText = button.textContent;
            button.disabled = true;
            button.innerHTML = 'Starting<span class="loading-spinner"></span>';

            try {
                const response = await authFetch(`/api/restart/${encodeURIComponent(directory)}`);
                const result = await response.json();

                if (result.success) {
                    button.innerHTML = '✅ Started!';
                    button.style.background = '#00b894';

                    // Always open the URL in a new tab
                    const portToOpen = result.lastPort || port;
                    if (portToOpen) {
                        setTimeout(() => {
                            const url = `http://${serverInfo.serverIp}:${portToOpen}`;
                            window.open(url, '_blank');
                        }, 1500);
                    }

                    // Reload directories after a delay
                    setTimeout(() => {
                        loadDirectories();
                        loadPorts();
                        button.textContent = originalText;
                        button.style.background = '';
                        button.disabled = false;
                    }, 3000);
                } else {
                    button.textContent = '❌ Failed';
                    button.style.background = '#d63031';
                    setTimeout(() => {
                        button.textContent = originalText;
                        button.style.background = '';
                        button.disabled = false;
                    }, 3000);
                    alert(`❌ Failed to restart: ${result.error || result.message}`);
                }
            } catch (err) {
                button.textContent = '❌ Error';
                button.disabled = false;
                setTimeout(() => {
                    button.textContent = originalText;
                }, 3000);
                alert(`❌ Error: ${err.message}`);
            }
        }

        function openApp(port) {
            const url = `http://${serverInfo.serverIp}:${port}`;
            window.open(url, '_blank');
        }

        async function stopPort(port) {
            if (!confirm(`Are you sure you want to stop the process on port ${port}?`)) {
                return;
            }

            try {
                const response = await authFetch(`/api/stop-port/${port}`, {
                    method: 'POST'
                });
                const result = await response.json();

                if (result.success) {
                    alert(`✅ Stopped process on port ${port}`);
                    loadDirectories();
                    loadPorts();
                } else {
                    alert(`❌ Error: ${result.error || 'Failed to stop process'}`);
                }
            } catch (err) {
                alert(`❌ Error: ${err.message}`);
            }
        }

        function openAssignPortModal(directory, currentPort, appName) {
            document.getElementById('assignDirPath').value = directory;
            document.getElementById('assignPort').value = currentPort || '';
            document.getElementById('assignAppName').value = appName || '';
            document.getElementById('assignPortModal').classList.add('active');
        }

        function closeAssignPortModal() {
            document.getElementById('assignPortModal').classList.remove('active');
            document.getElementById('assignPortForm').reset();
        }

        async function handleAssignPort(event) {
            event.preventDefault();

            const directory = document.getElementById('assignDirPath').value;
            const port = document.getElementById('assignPort').value;
            const appName = document.getElementById('assignAppName').value.trim();

            try {
                const response = await authFetch('/api/assign-port', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        directory: directory,
                        port: port,
                        appName: appName || undefined
                    })
                });

                const result = await response.json();

                if (result.success) {
                    closeAssignPortModal();
                    alert(`✅ Port ${port} assigned to ${directory}`);
                    loadDirectories();
                    loadPorts();
                } else {
                    alert(`❌ Error: ${result.error}`);
                }
            } catch (err) {
                alert(`❌ Error: ${err.message}`);
            }
        }

        async function scanAllDirectories() {
            document.getElementById('scanDirModal').classList.add('active');
            document.getElementById('scanDirList').innerHTML = `
                <div style="text-align: center; padding: 20px;">
                    <div class="spinner"></div>
                    <p>Scanning directories...</p>
                </div>
            `;

            try {
                const response = await authFetch('/api/scan-directories');
                const result = await response.json();

                if (result.directories && result.directories.length > 0) {
                    const listHTML = result.directories.map(dir => `
                        <div class="dir-item" style="margin-bottom: 10px;">
                            <div class="dir-info" style="flex: 1;">
                                <div class="dir-path" style="font-size: 0.95em;">📁 ${dir}</div>
                            </div>
                            <div class="dir-actions">
                                <button class="dir-action-btn" style="padding: 6px 12px; font-size: 0.8em;" onclick="addScannedDirectory('${dir}')">➕ Add</button>
                            </div>
                        </div>
                    `).join('');

                    document.getElementById('scanDirList').innerHTML = listHTML;
                } else {
                    document.getElementById('scanDirList').innerHTML = `
                        <div class="empty-directories">
                            No Node.js projects found
                        </div>
                    `;
                }
            } catch (err) {
                document.getElementById('scanDirList').innerHTML = `
                    <div class="error">Error: ${err.message}</div>
                `;
            }
        }

        function closeScanDirModal() {
            document.getElementById('scanDirModal').classList.remove('active');
        }

        async function addScannedDirectory(directory) {
            // Suggest an available port
            let suggestedPort = '3000';
            try {
                const portResponse = await authFetch('/api/suggest-port');
                const portData = await portResponse.json();
                suggestedPort = portData.port;
            } catch (err) {
                console.error('Failed to get suggested port:', err);
            }

            const port = prompt(`Enter port number for ${directory} (suggested: ${suggestedPort}):`, suggestedPort);
            if (!port) return;

            try {
                const response = await authFetch('/api/add-directory', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        directory: directory,
                        port: port,
                        appName: directory.split('/').pop()
                    })
                });

                const result = await response.json();

                if (result.success) {
                    alert(`✅ Directory added: ${directory}`);
                    closeScanDirModal();
                    loadDirectories();
                    loadPorts();
                } else {
                    alert(`❌ Error: ${result.error}`);
                }
            } catch (err) {
                alert(`❌ Error: ${err.message}`);
            }
        }

        // Close modals when clicking outside
        document.getElementById('assignPortModal').addEventListener('click', (e) => {
            if (e.target.id === 'assignPortModal') {
                closeAssignPortModal();
            }
        });

        document.getElementById('scanDirModal').addEventListener('click', (e) => {
            if (e.target.id === 'scanDirModal') {
                closeScanDirModal();
            }
        });

        // Ask the Server functionality
        async function askServer() {
            const input = document.getElementById('askInput');
            const btn = document.getElementById('askBtn');
            const responseDiv = document.getElementById('askResponse');
            const query = input.value.trim();

            if (!query) return;

            btn.disabled = true;
            btn.textContent = 'Thinking...';
            responseDiv.innerHTML = '';
            responseDiv.classList.remove('show');

            try {
                const response = await authFetch('/api/ask', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ query })
                });

                const result = await response.json();

                if (result.success) {
                    responseDiv.innerHTML = `
                        <div class="response-header">📊 ${result.query || query}</div>
                        <div class="response-content">${result.response}</div>
                    `;
                } else {
                    responseDiv.innerHTML = `
                        <div class="response-error">❌ ${result.error}</div>
                    `;
                }

                responseDiv.classList.add('show');
            } catch (err) {
                responseDiv.innerHTML = `
                    <div class="response-error">❌ Error: ${err.message}</div>
                `;
                responseDiv.classList.add('show');
            } finally {
                btn.disabled = false;
                btn.textContent = 'Send 🚀';
            }
        }

        function quickQuery(type) {
            const input = document.getElementById('askInput');
            const queries = {
                'uptime': 'What is the uptime?',
                'memory': 'Show memory usage',
                'cpu': 'Show CPU usage',
                'disk': 'Show disk usage',
                'ports': 'Show open ports',
                'processes': 'Show top processes',
                'help': 'help'
            };
            input.value = queries[type] || type;
            askServer();
        }

        // Preview functionality
        let previewTimeout = null;
        let hideTimeout = null;

        function showPreview(url, title) {
            clearTimeout(hideTimeout);

            previewTimeout = setTimeout(() => {
                const overlay = document.getElementById('previewOverlay');
                const container = document.getElementById('previewContainer');
                const iframe = document.getElementById('previewIframe');
                const titleEl = document.getElementById('previewTitle');

                titleEl.textContent = title;
                iframe.src = url;

                overlay.classList.add('active');
                container.classList.add('active');
            }, 500); // Show preview after 500ms hover
        }

        function hidePreviewDelayed() {
            clearTimeout(previewTimeout);

            hideTimeout = setTimeout(() => {
                closePreview();
            }, 300);
        }

        function closePreview() {
            const overlay = document.getElementById('previewOverlay');
            const container = document.getElementById('previewContainer');
            const iframe = document.getElementById('previewIframe');

            overlay.classList.remove('active');
            container.classList.remove('active');

            // Clear iframe after animation
            setTimeout(() => {
                if (!container.classList.contains('active')) {
                    iframe.src = '';
                }
            }, 300);
        }

        // Prevent closing when hovering over preview
        document.getElementById('previewContainer').addEventListener('mouseenter', () => {
            clearTimeout(hideTimeout);
        });

        document.getElementById('previewContainer').addEventListener('mouseleave', () => {
            closePreview();
        });

        // Add Directory Modal Functions
        function openAddDirectoryModal() {
            document.getElementById('addDirModal').classList.add('active');
        }

        function closeAddDirectoryModal() {
            document.getElementById('addDirModal').classList.remove('active');
            document.getElementById('addDirForm').reset();
        }

        async function handleAddDirectory(event) {
            event.preventDefault();

            const directory = document.getElementById('dirPath').value.trim();
            const port = document.getElementById('dirPort').value.trim();
            const appName = document.getElementById('dirAppName').value.trim();

            try {
                const response = await authFetch('/api/add-directory', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        directory: directory,
                        port: port || undefined,
                        appName: appName || undefined
                    })
                });

                const result = await response.json();

                if (result.success) {
                    closeAddDirectoryModal();
                    alert(`✅ Directory added: ${directory}`);
                    loadPorts();
                    loadDirectories();
                } else {
                    alert(`❌ Error: ${result.error}`);
                }
            } catch (err) {
                alert(`❌ Error: ${err.message}`);
            }
        }

        // Close modal when clicking outside
        document.getElementById('addDirModal').addEventListener('click', (e) => {
            if (e.target.id === 'addDirModal') {
                closeAddDirectoryModal();
            }
        });

        // Web View Toggle Functions
        function toggleWebView(port, url) {
            const container = document.getElementById(`webview-${port}`);
            const iframe = document.getElementById(`iframe-${port}`);
            const button = event.target.closest('.web-view-toggle');

            if (container.classList.contains('active')) {
                // Hide the web view
                container.classList.remove('active');
                button.classList.remove('active');
                button.innerHTML = '<span>👁️ Show Live Preview</span>';
                iframe.src = ''; // Clear iframe to stop loading
            } else {
                // Show the web view
                container.classList.add('active');
                button.classList.add('active');
                button.innerHTML = '<span>👁️ Hide Live Preview</span>';

                // Load iframe if not already loaded
                if (!iframe.src) {
                    iframe.src = url;

                    // Hide loading spinner when iframe loads
                    iframe.onload = function() {
                        const loading = container.querySelector('.web-view-loading');
                        if (loading) loading.style.display = 'none';
                    };

                    // Handle iframe errors
                    iframe.onerror = function() {
                        const loading = container.querySelector('.web-view-loading');
                        if (loading) {
                            loading.innerHTML = '<div class="web-view-error">❌ Failed to load preview</div>';
                        }
                    };
                }
            }
        }

        function openFullscreen(url, title) {
            const overlay = document.getElementById('previewOverlay');
            const container = document.getElementById('previewContainer');
            const iframe = document.getElementById('previewIframe');
            const titleEl = document.getElementById('previewTitle');

            titleEl.textContent = title;
            iframe.src = url;

            overlay.classList.add('active');
            container.classList.add('active');
        }

        // Restart app function
        async function restartApp(directory, port) {
            if (!confirm(`Are you sure you want to restart the app in ${directory}?`)) {
                return;
            }

            const button = event.target;
            const card = button.closest('.card');

            button.disabled = true;
            button.innerHTML = '⏳ Starting<span class="loading-spinner"></span>';

            // Add loading state to card
            card.classList.add('loading');
            const overlay = document.createElement('div');
            overlay.className = 'loading-overlay';
            overlay.innerHTML = `
                <div class="big-spinner"></div>
                <div class="loading-text">🚀 Starting app...</div>
            `;
            card.appendChild(overlay);

            try {
                const response = await authFetch(`/api/restart/${encodeURIComponent(directory)}`);
                const result = await response.json();

                if (result.success) {
                    overlay.querySelector('.loading-text').textContent = '✅ Started successfully!';
                    button.innerHTML = '✅ Started!';
                    button.style.background = 'linear-gradient(90deg, #00b894 0%, #00a085 100%)';

                    // ALWAYS open the URL in a new tab using server IP
                    const portToOpen = result.lastPort || port;
                    if (portToOpen) {
                        setTimeout(() => {
                            const url = `http://${serverInfo.serverIp}:${portToOpen}`;
                            window.open(url, '_blank');
                        }, 1500);
                    }

                    setTimeout(() => {
                        card.classList.remove('loading');
                        overlay.remove();
                        button.textContent = '🔄 Restart App';
                        button.style.background = 'linear-gradient(90deg, #ff6b6b 0%, #ee5a6f 100%)';
                        button.disabled = false;

                        // Reload ports after restart
                        setTimeout(() => {
                            loadPorts();
                            loadDirectories();
                        }, 2000);
                    }, 3000);
                } else {
                    overlay.querySelector('.loading-text').textContent = '❌ Failed to start';
                    button.textContent = '❌ Failed';
                    button.style.background = '#d63031';
                    setTimeout(() => {
                        card.classList.remove('loading');
                        overlay.remove();
                        button.textContent = '🔄 Restart App';
                        button.style.background = 'linear-gradient(90deg, #ff6b6b 0%, #ee5a6f 100%)';
                        button.disabled = false;
                    }, 3000);
                    alert(`Failed to restart: ${result.error || result.message}`);
                }
            } catch (err) {
                card.classList.remove('loading');
                if (overlay && overlay.parentNode) overlay.remove();
                button.textContent = '❌ Error';
                button.disabled = false;
                alert(`Error: ${err.message}`);
                setTimeout(() => {
                    button.textContent = '🔄 Restart App';
                }, 3000);
            }
        }
    </script>
</body>
</html>