<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Docker P2P Manager</title> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet"> <!-- xterm.css for Terminal --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css"> <style> body { margin: 0; display: flex; height: 100vh; background-color: #1a1a1a; color: white; overflow: hidden; } .hidden { display: none !important; } #titlebar { -webkit-app-region: drag; height: 30px; width: 100%; position: fixed; top: 0; background-color: #2c2c2c; z-index: 1000; } pear-ctrl[data-platform="darwin"] { float: left; margin-top: 5px; margin-left: 10px; } #sidebar { position: fixed; top: 30px; left: 0; background-color: #2c2c2c; height: calc(100vh - 30px); width: 250px; overflow-y: auto; transition: width 0.3s ease-in-out; } #sidebar.collapsed { width: 50px; } #sidebar.collapsed .content { display: none; } #collapse-sidebar-btn { position: absolute; top: 10px; right: 10px; background-color: #444; border: none; color: white; width: 30px; height: 30px; border-radius: 50%; font-size: 16px; line-height: 30px; text-align: center; cursor: pointer; } #content { display: flex; flex-direction: column; /* Keep vertical stacking for child elements */ margin-left: 250px; /* Leave space for the sidebar */ flex: 1; /* Allow the content to grow */ overflow-y: auto; /* Allow scrolling if content overflows */ position: relative; } #sidebar.collapsed~#content { margin-left: 50px; } .connection-status { border-radius: 50%; width: 10px; height: 10px; display: inline-block; margin-right: 8px; } .status-connected { background-color: green; } .status-disconnected { background-color: red; } #terminal-modal { position: fixed; bottom: 0; left: 0; width: 100%; max-height: 90vh; height: 300px; background-color: #1a1a1a; border-top: 2px solid #444; display: none; flex-direction: column; z-index: 1000; overflow: hidden; } #terminal-modal .header { background-color: #444; cursor: move; padding: 10px; display: flex; justify-content: space-between; align-items: center; } #terminal-resize-handle { width: 100%; height: 10px; cursor: ns-resize; background-color: #444; position: absolute; bottom: 0; left: 0; } #terminal-container { flex: 1; overflow: hidden; background-color: black; color: white; } #tray { position: fixed; bottom: 0; left: 0; width: 100%; background-color: #444; padding: 5px 10px; display: flex; gap: 10px; overflow-x: auto; white-space: nowrap; z-index: 999; } #tray .tray-item { background-color: #555; color: white; padding: 5px 10px; border-radius: 5px; cursor: pointer; } #status-indicator { display: none; /* Ensure it's hidden by default */ position: fixed; top: 0; left: 0; width: 100%; height: 100%; align-items: center; justify-content: center; background-color: rgba(0, 0, 0, 0.75); z-index: 1050; } #status-indicator .spinner-border { width: 3rem; height: 3rem; } #status-indicator p { margin-top: 1rem; color: #fff; font-size: 1.25rem; } #welcome-page { display: flex; flex-direction: column; /* Stack child elements vertically */ justify-content: center; /* Center content vertically */ align-items: center; /* Center content horizontally */ text-align: center; /* Center-align text */ position: absolute; /* Overlay it over the content area */ top: 50%; left: 50%; transform: translate(-50%, -50%); /* Center it perfectly in the content */ max-width: 800px; /* Restrict the width of the welcome page */ width: 100%; /* Allow it to scale */ padding: 20px; background-color: transparent; /* Match the theme */ } #welcome-page.hidden { display: none !important; /* Completely hide when not needed */ } #dashboard { display: flex; /* Use flex layout for content within the dashboard */ flex-direction: column; /* Stack elements vertically */ flex: 1; /* Ensure it uses all available space */ width: 100%; /* Take up the full width of the content area */ margin-top: 30px; /* Remove extra padding */ overflow-y: auto; /* Allow vertical scrolling if needed */ position: relative; /* Prevent overlap with other elements */ } #dashboard.hidden { display: none !important; /* Hide the dashboard completely when not needed */ } #alert-container { position: fixed; bottom: 20px; right: 20px; z-index: 1055; /* Ensure it overlays important elements only */ display: flex; flex-direction: column-reverse; /* Stack alerts upwards */ gap: 10px; /* Add space between alerts */ pointer-events: none; /* Prevent container from blocking clicks */ } .alert { display: flex; align-items: center; justify-content: space-between; max-width: 80%; padding: 12px 20px; background-color: #2b2b2b; color: #e0e0e0; border-radius: 6px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5); font-family: Arial, sans-serif; font-size: 14px; animation: fadeIn 0.3s ease-out, fadeOut 4.5s ease-in forwards; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: auto; /* Allow alerts to be interactive */ } .alert.success { border-left: 6px solid #28a745; /* Green border for success */ } .alert.danger { border-left: 6px solid #dc3545; /* Red border for danger */ } .alert .close-btn { background: none; border: none; color: #e0e0e0; font-size: 16px; cursor: pointer; margin-left: auto; /* Align to the far right */ } @media (max-width: 768px) { #alert-container { bottom: 10px; right: 10px; } .alert { max-width: 90%; font-size: 12px; padding: 10px 15px; white-space: normal; /* Allow wrapping on small screens */ text-overflow: clip; /* Disable ellipsis when wrapping */ } } @media (min-width: 768px) { .alert { white-space: nowrap; /* Re-enable nowrap for larger screens */ text-overflow: ellipsis; /* Add ellipsis for overflowed text */ } } #sidebar.collapsed .btn-danger { display: none; } /* General scrollbar styles for dark and skinny scrollbars */ * { scrollbar-width: thin; /* Firefox */ scrollbar-color: #555 #1a1a1a; /* Firefox: thumb and track color */ } /* WebKit-based browsers (Chrome, Edge, Safari) */ *::-webkit-scrollbar { width: 8px; /* Width of vertical scrollbar */ height: 8px; /* Height of horizontal scrollbar */ } *::-webkit-scrollbar-track { background: #1a1a1a; /* Track color */ } *::-webkit-scrollbar-thumb { background-color: #555; /* Thumb color */ border-radius: 10px; /* Round the thumb */ border: 2px solid #1a1a1a; /* Space between thumb and track */ } *::-webkit-scrollbar-thumb:hover { background-color: #777; /* Lighter color on hover */ } *::-webkit-scrollbar-thumb:active { background-color: #999; /* Even lighter color when active */ } </style> </head> <body> <div id="titlebar"> <pear-ctrl></pear-ctrl> </div> <div id="sidebar"> <button id="collapse-sidebar-btn"><</button> <div class="content"> <h4 class="text-center mt-3">Connections</h4> <ul id="connection-list" class="list-group mb-3"></ul> <form id="add-connection-form" class="px-3"> <input type="text" id="new-connection-topic" class="form-control mb-2" placeholder="Enter server topic" required> <button type="submit" class="btn btn-primary w-100">Add Connection</button> </form> </div> </div> <div id="content"> <div id="welcome-page"> <h1>Welcome to Peartainer</h1> <p class="mt-3">Easily manage your Docker containers across peer-to-peer connections.</p> <p>To get started, add a connection using the form in the sidebar.</p> <!-- <img src="https://via.placeholder.com/500x300" alt="Welcome Graphic" class="img-fluid mt-4"> --> </div> <div id="dashboard" class="hidden"> <div class="table-responsive"> <table class="table table-dark table-striped"> <thead> <tr> <th>Name</th> <th>Image</th> <th>Status</th> <th>CPU (%)</th> <th>Memory (MB)</th> <th>IP Address</th> <th>Actions</th> </tr> </thead> <tbody id="container-list"></tbody> </table> </div> </div> </div> <!-- Duplicate Container Modal --> <div class="modal fade" id="duplicateModal" tabindex="-1" aria-labelledby="duplicateModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content bg-dark text-white"> <div class="modal-header"> <h5 class="modal-title" id="duplicateModalLabel">Duplicate Container</h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form id="duplicate-container-form"> <div class="mb-3"> <label for="container-name" class="form-label">Container Name</label> <input type="text" class="form-control" id="container-name" required> </div> <div class="mb-3"> <label for="container-hostname" class="form-label">Hostname</label> <input type="text" class="form-control" id="container-hostname" required> </div> <div class="mb-3"> <label for="container-image" class="form-label">Image</label> <input type="text" class="form-control" id="container-image" required> </div> <div class="mb-3"> <label for="container-netmode" class="form-label">Net Mode</label> <input type="text" class="form-control" id="container-netmode" required> </div> <div class="mb-3"> <label for="container-cpu" class="form-label">CPU Count</label> <input type="number" class="form-control" id="container-cpu" required> </div> <div class="mb-3"> <label for="container-memory" class="form-label">Memory (MB)</label> <input type="number" class="form-control" id="container-memory" required> </div> <div class="mb-3"> <label for="container-config" class="form-label">Container Configuration (JSON)</label> <textarea class="form-control" id="container-config" rows="10" required></textarea> </div> <button type="submit" class="btn btn-primary">Deploy Duplicate</button> </form> </div> </div> </div> </div> <!-- Terminal Modal --> <div id="terminal-modal"> <div class="header"> <span id="terminal-title"></span> <div> <button id="kill-terminal-btn" class="btn btn-sm btn-danger"> <i class="fas fa-times-circle"></i> </button> </div> </div> <div id="terminal-container"></div> <div id="terminal-resize-handle"></div> </div> <!-- Delete Confirmation Modal --> <div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true"> <div class="modal-dialog modal-sm"> <div class="modal-content bg-dark text-white"> <div class="modal-header"> <h5 class="modal-title" id="deleteModalLabel">Confirm Deletion</h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> Are you sure you want to delete this container? </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> <button type="button" id="confirm-delete-btn" class="btn btn-danger">Delete</button> </div> </div> </div> </div> <!-- Status Indicator Overlay --> <div id="status-indicator" class="position-fixed top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-dark bg-opacity-75" style="display: none; z-index: 1050;"> <div class="text-center"> <div class="spinner-border text-light" role="status"> <span class="visually-hidden">Loading...</span> </div> <p class="mt-3 text-light">Processing...</p> </div> </div> <!-- Alert Container --> <div id="alert-container" class="position-fixed top-0 start-50 translate-middle-x mt-3" style="z-index: 1051; max-width: 90%;"></div> <!-- xterm.js --> <script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.js"></script> <!-- Bootstrap JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> <!-- Your App JS --> <script type="module" src="app.js"></script> </body> </html>