1
0
mirror of https://github.com/boku7/Loki synced 2026-06-06 15:24:27 +00:00
Files
boku7-Loki/client/dashboard.html
T

425 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loki Dashboard</title>
<style>
body {
background-image: url('./assets/images/background.png');
background-size: contain; /* Fit the image within the element */
background-repeat: no-repeat; /* Prevent repeating */
background-position: center; /* Center the image */
height: 100vh; /* Full viewport height */
background-color: #000000;
color: #e0e0e0;
font-family: 'Arial', sans-serif;
display: flex;
justify-content: center;
align-items: flex-start;
height: 100vh;
margin: 0;
overflow: auto; /* Allow both vertical and horizontal scrolling */
}
/* Dark mode scrollbar for the body */
body::-webkit-scrollbar {
width: 12px;
}
body::-webkit-scrollbar-track {
background: #0a0a0a;
border-radius: 6px;
}
body::-webkit-scrollbar-thumb {
background: #333333;
border-radius: 6px;
border: 2px solid #0a0a0a;
}
body::-webkit-scrollbar-thumb:hover {
background: #555555;
}
table.vampire-matrix {
width: 100%; /* Make the table consume the entire window width */
min-width: 1200px; /* Minimum width to ensure all columns are readable */
border-collapse: collapse;
background-color: #1c1c1c;
box-shadow: 0 0 10px rgba(131, 131, 131, 0.5);
max-height: none; /* Allow table to grow beyond viewport */
}
table.vampire-matrix th,
table.vampire-matrix td {
border: 1px solid #333;
padding: 10px 15px;
text-align: left;
vertical-align: middle;
word-wrap: break-word; /* Ensure content wraps within the cell */
}
table.vampire-matrix thead th {
background-color: #333;
color: #fff;
font-size: 1.1em;
}
table.vampire-matrix tbody tr {
transition: background-color 0.2s ease, border-left-color 0.2s ease;
cursor: pointer;
border-left: 3px solid transparent;
}
table.vampire-matrix tbody tr:hover,
table.vampire-matrix tbody tr.row-hovered {
background-color: #2a2a2a !important;
border-left-color: #06883e !important;
}
table.vampire-matrix tbody tr:active {
background-color: #333333 !important;
}
/* Hide the Container column (2nd column) */
table.vampire-matrix th:nth-child(2),
table.vampire-matrix td:nth-child(2) {
display: none;
}
table.vampire-matrix tbody tr:nth-child(even) {
background-color: #232323;
}
table.vampire-matrix tbody tr:nth-child(odd) {
background-color: #1a1a1a;
}
table.vampire-matrix td {
color: #fff; /* Change text color to white */
}
table.vampire-matrix th {
border-bottom: 2px solid #444;
}
table.vampire-matrix tbody td:first-child {
border-left: 2px solid #444;
}
table.vampire-matrix tbody td:last-child {
border-right: 2px solid #444;
}
tr {
height: 20px;
max-height: 20px; /* Set your desired max-height here */
overflow: hidden;
}
tr > td {
max-height: inherit; /* Apply max height to each cell */
overflow: hidden; /* Ensure content overflow is hidden in each cell */
}
/* Dark mode context menu styling */
.context-menu {
position: fixed;
background-color: #2d2d2d;
border: 1px solid #555;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
padding: 4px 0;
min-width: 150px;
z-index: 9999;
font-family: 'Arial', sans-serif;
font-size: 14px;
color: #e0e0e0;
}
.context-menu-item {
padding: 8px 16px;
cursor: pointer;
transition: background-color 0.2s ease;
border: none;
background: none;
width: 100%;
text-align: left;
color: #e0e0e0;
}
.context-menu-item:hover {
background-color: #404040;
}
.context-menu-item:active {
background-color: #4a4a4a;
}
.context-menu-separator {
height: 1px;
background-color: #555;
margin: 4px 0;
}
/* Override Electron's native context menu when possible */
::-webkit-context-menu {
background-color: #2d2d2d !important;
border: 1px solid #555 !important;
border-radius: 4px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6) !important;
}
::-webkit-context-menu-item {
background-color: #2d2d2d !important;
color: #e0e0e0 !important;
padding: 8px 16px !important;
}
::-webkit-context-menu-item:hover {
background-color: #404040 !important;
}
/* Additional dark scrollbar for any dropdowns */
*::-webkit-scrollbar {
width: 8px;
}
*::-webkit-scrollbar-track {
background: #1a1a1a;
}
*::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
*::-webkit-scrollbar-thumb:hover {
background: #666;
}
.logo-watermark {
position: fixed;
bottom: 20px;
right: 20px;
width: auto;
height: 60px;
opacity: 0.6;
z-index: 1000;
pointer-events: auto;
cursor: pointer;
transition: opacity 0.3s ease;
}
.logo-watermark:hover {
opacity: 0.8;
}
.repo-watermark {
position: fixed;
bottom: 20px;
left: 20px;
width: auto;
height: 60px;
opacity: 0.6;
z-index: 1000;
pointer-events: auto;
cursor: pointer;
transition: opacity 0.3s ease;
}
.repo-watermark:hover {
opacity: 0.8;
}
/* Hide logos when window height is too small */
@media (max-height: 120px) {
.logo-watermark,
.repo-watermark {
display: none !important;
}
}
</style>
</head>
<body>
<table id="containerTable" class="vampire-matrix" height="20px">
<thead>
<tr>
<th data-column="agentid">Agent ID <span class="arrow" id="agentidArrow"></span></th>
<th data-column="container">Container <span class="arrow" id="containerArrow"></span></th>
<th data-column="hostname">Hostname <span class="arrow" id="hostnameArrow"></span></th>
<th data-column="username">Username <span class="arrow" id="usernameArrow"></span></th>
<th data-column="process">Process <span class="arrow" id="processArrow"></span></th>
<th data-column="pid">PID <span class="arrow" id="pidArrow"></span></th>
<th data-column="ip">IP <span class="arrow" id="ipArrow"></span></th>
<th data-column="arch">Arch <span class="arrow" id="archArrow"></span></th>
<th data-column="platform">Platform <span class="arrow" id="platformArrow"></span></th>
<th data-column="mode">Mode <span class="arrow" id="modeArrow"></span></th>
<th data-column="checkin">Checkin <span class="arrow" id="checkinArrow"></span></th>
</tr>
</thead>
<tbody>
<!-- Container rows will be added here by renderer.js -->
</tbody>
</table>
<!-- Logo watermark -->
<img src="./assets/images/logo1.png" alt="Loki Logo" class="logo-watermark" onclick="openTwitterProfile()">
<!-- Repo watermark -->
<img src="./assets/images/repo.png" alt="Repo Logo" class="repo-watermark" onclick="openGitHubRepo()">
<script src="dashboard.js"></script>
<script>
const { shell } = require('electron');
// Function to open GitHub repository
function openGitHubRepo() {
shell.openExternal('https://github.com/boku7/loki');
}
// Function to open Twitter/X profile
function openTwitterProfile() {
shell.openExternal('https://x.com/0xBoku');
}
// Function to apply background image to dashboard
function applyBackgroundImage(settings) {
if (settings && settings.backgroundImage) {
console.log('Applying background image:', settings.backgroundImage);
document.body.style.backgroundImage = `url('${settings.backgroundImage}')`;
} else {
console.log('Clearing background image');
document.body.style.backgroundImage = "url('./assets/images/background.png')";
}
}
// Handle font settings from agent settings window
ipcRenderer.on('apply-font-settings', (event, settings) => {
console.log('=== RECEIVED APPLY-FONT-SETTINGS IPC IN DASHBOARD ===');
console.log('Settings:', settings);
applyBackgroundImage(settings);
});
// Logo visibility management based on window height and width
function manageLogoVisibility() {
const logoWatermark = document.querySelector('.logo-watermark');
const repoWatermark = document.querySelector('.repo-watermark');
const minHeightNeeded = 140; // 60px logo + 40px padding + some buffer
function checkLogoVisibility() {
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
// Hide both logos if window height is too small
const shouldHideForHeight = windowHeight < minHeightNeeded;
let shouldHideRepoForWidth = false;
// Calculate actual overlap positions if both logos exist
if (logoWatermark && repoWatermark && !shouldHideForHeight) {
// Ensure logos are visible first to get accurate measurements
logoWatermark.style.display = 'block';
repoWatermark.style.display = 'block';
// Get actual logo dimensions and positions
const logoRect = logoWatermark.getBoundingClientRect();
const repoRect = repoWatermark.getBoundingClientRect();
// Calculate positions:
// Left logo: positioned at left: 20px, so its right edge is at (20px + width)
// Right logo: positioned at right: 20px, so its left edge is at (windowWidth - 20px - width)
const repoRightEdge = 20 + repoRect.width;
const logoLeftEdge = windowWidth - 20 - logoRect.width;
// Hide repo logo if its right edge would touch or overlap the right logo's left edge
shouldHideRepoForWidth = repoRightEdge >= logoLeftEdge;
}
if (logoWatermark) {
logoWatermark.style.display = shouldHideForHeight ? 'none' : 'block';
}
if (repoWatermark) {
// Hide repo logo if either height is too small OR width would cause overlap
repoWatermark.style.display = (shouldHideForHeight || shouldHideRepoForWidth) ? 'none' : 'block';
}
}
// Check on load and resize
checkLogoVisibility();
window.addEventListener('resize', checkLogoVisibility);
}
// Persistent row hover management
function setupPersistentHover() {
const table = document.getElementById('containerTable');
if (!table) return;
const tbody = table.querySelector('tbody');
// Add event listeners for persistent hover
tbody.addEventListener('mouseenter', (e) => {
if (e.target.tagName === 'TD') {
const row = e.target.closest('tr');
if (row && row.parentElement === tbody) {
// Remove hover class from all other rows first
tbody.querySelectorAll('.row-hovered').forEach(otherRow => {
if (otherRow !== row) {
otherRow.classList.remove('row-hovered');
}
});
// Add hover class to current row
row.classList.add('row-hovered');
}
}
}, true);
tbody.addEventListener('mouseleave', (e) => {
if (e.target.tagName === 'TD') {
const row = e.target.closest('tr');
if (row && row.parentElement === tbody) {
// Check if mouse is still within the table area
const rect = table.getBoundingClientRect();
const mouseX = e.clientX;
const mouseY = e.clientY;
if (mouseX < rect.left || mouseX > rect.right ||
mouseY < rect.top || mouseY > rect.bottom) {
row.classList.remove('row-hovered');
}
}
}
}, true);
// Clean up hover states when mouse leaves table entirely
table.addEventListener('mouseleave', () => {
tbody.querySelectorAll('.row-hovered').forEach(row => {
row.classList.remove('row-hovered');
});
});
}
// Load saved settings on page load
window.addEventListener('DOMContentLoaded', () => {
// Setup persistent hover system
setupPersistentHover();
// Setup logo visibility management
manageLogoVisibility();
// Load settings from customize.js file
ipcRenderer.invoke('get-customize-settings').then(settings => {
if (settings) {
console.log('Loading customize settings for dashboard:', settings);
applyBackgroundImage(settings);
}
}).catch(error => {
console.error('Error loading customize settings in dashboard:', error);
});
});
</script>
</body>
</html>