refactor(dashboard): overhaul UI styling, typography, layout and fix JS string syntax error

This commit is contained in:
Riz Ashraf committed 2026-09-12 11:25:44 +01:00
1 parent 5d37af1d74
commit bd4622bbb5
1 file changed
+267 -61
+267 -61
View File
@@ -1,56 +1,241 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta charset="UTF-8">
<title>Antigravity Memory Hub</title> <title>Antigravity Memory Hub</title>
<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
<style> <style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f0f2f5; color: #333; display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; } body {
.header-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
h1 { margin: 0; color: #2c3e50; } margin: 0;
.status-dot { display: inline-block; width: 10px; height: 10px; background-color: #2ecc71; border-radius: 50%; margin-right: 8px; box-shadow: 0 0 5px #2ecc71; } padding: 20px;
background-color: #f4f7f6;
.main-container { display: flex; gap: 20px; flex: 1; min-height: 0; } color: #2c3e50;
.panel { background: white; padding: 15px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); display: flex; flex-direction: column; } display: flex;
.panel h2 { margin-top: 0; margin-bottom: 15px; color: #34495e; font-size: 1.2em; border-bottom: 1px solid #ecf0f1; padding-bottom: 10px; } flex-direction: column;
height: 100vh;
/* Graph Panel */ box-sizing: border-box;
.graph-panel { flex: 2; position: relative; display: flex; flex-direction: column; } }
#network-container { flex: 1; min-height: 400px; position: relative; border: 1px solid #bdc3c7; border-radius: 4px; background: #fafafa; } .header-bar {
display: flex;
/* Right Column */ justify-content: space-between;
.right-column { flex: 1; display: flex; flex-direction: column; gap: 20px; } align-items: center;
background: white;
/* Search Panel */ padding: 15px 25px;
.search-panel { flex: 1; } border-radius: 8px;
input[type="text"] { width: 100%; padding: 10px; border: 1px solid #bdc3c7; border-radius: 4px; box-sizing: border-box; margin-bottom: 10px; } box-shadow: 0 2px 10px rgba(0,0,0,0.05);
#search-results { overflow-y: auto; flex: 1; font-size: 0.9em; } margin-bottom: 20px;
.search-result-item { padding: 8px; border-bottom: 1px solid #eee; } }
.search-result-item strong { color: #2980b9; display: block; } .header-bar h1 {
margin: 0;
/* Kanban Panel */ font-size: 1.5em;
.kanban-panel { flex: 1.5; display: flex; flex-direction: column; min-height: 0; } color: #2c3e50;
.kanban-board { display: flex; gap: 10px; flex: 1; min-height: 0; } display: flex;
.kanban-column { flex: 1; background: #f8f9fa; border-radius: 4px; padding: 10px; display: flex; flex-direction: column; min-height: 0; } align-items: center;
.kanban-column h3 { margin: 0 0 10px 0; font-size: 0.9em; color: #7f8c8d; text-align: center; } gap: 10px;
.kanban-items { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; min-height: 0; padding-right: 5px; } }
.status-dot {
/* Graph Controls */ height: 12px;
.graph-controls { position: absolute; top: 50px; right: 30px; z-index: 10; display: flex; flex-direction: column; gap: 5px; } width: 12px;
.graph-controls button { background: white; border: 1px solid #bdc3c7; border-radius: 4px; width: 30px; height: 30px; cursor: pointer; font-weight: bold; } background-color: #2ecc71;
.graph-controls button:hover { background: #ecf0f1; } border-radius: 50%;
.task-card { background: white; padding: 8px; border-radius: 4px; border-left: 4px solid #3498db; font-size: 0.85em; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } display: inline-block;
.task-card.completed { border-left-color: #2ecc71; opacity: 0.7; } margin-right: 8px;
box-shadow: 0 0 5px #2ecc71;
/* Activity Feed */ }
.activity-panel { height: 200px; margin-top: 20px; } .main-container {
#activity-feed { overflow-y: auto; flex: 1; font-family: monospace; font-size: 0.85em; background: #1e1e1e; color: #00ff00; padding: 10px; border-radius: 4px; } display: flex;
.feed-entry { margin-bottom: 4px; } flex: 1;
gap: 20px;
min-height: 0;
}
.panel {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
display: flex;
flex-direction: column;
}
.panel h2 {
margin-top: 0;
margin-bottom: 15px;
color: #34495e;
font-size: 1.2em;
border-bottom: 2px solid #f0f2f5;
padding-bottom: 10px;
}
.graph-panel {
flex: 2;
position: relative;
}
#network-container {
flex: 1;
min-height: 400px;
position: relative;
border: 1px solid #e1e8ed;
border-radius: 6px;
background: #fafbfc;
}
.graph-controls {
position: absolute;
top: 65px;
right: 35px;
z-index: 10;
display: flex;
flex-direction: column;
gap: 5px;
}
.graph-controls button {
background: white;
border: 1px solid #dcdde1;
border-radius: 4px;
width: 32px;
height: 32px;
cursor: pointer;
font-weight: bold;
color: #34495e;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: all 0.2s ease;
}
.graph-controls button:hover {
background: #f8f9fa;
border-color: #bdc3c7;
}
.right-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 20px;
min-width: 300px;
}
.search-panel {
flex: 1;
min-height: 200px;
}
input[type="text"] {
width: 100%;
padding: 12px;
border: 1px solid #dcdde1;
border-radius: 6px;
box-sizing: border-box;
margin-bottom: 15px;
font-size: 0.95em;
transition: border-color 0.2s ease;
}
input[type="text"]:focus {
outline: none;
border-color: #3498db;
}
#search-results {
overflow-y: auto;
flex: 1;
font-size: 0.9em;
}
.search-result-item {
padding: 12px;
border-bottom: 1px solid #f0f2f5;
}
.search-result-item:last-child {
border-bottom: none;
}
.search-result-item strong {
color: #2980b9;
display: block;
margin-bottom: 4px;
}
.kanban-panel {
flex: 1.5;
min-height: 300px;
}
.kanban-board {
display: flex;
gap: 15px;
flex: 1;
min-height: 0;
}
.kanban-column {
flex: 1;
background: #f8f9fa;
border-radius: 6px;
padding: 12px;
display: flex;
flex-direction: column;
min-height: 0;
border: 1px solid #e1e8ed;
}
.kanban-column h3 {
margin: 0 0 12px 0;
font-size: 0.85em;
color: #7f8c8d;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.kanban-items {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 10px;
padding-right: 5px;
}
.kanban-items::-webkit-scrollbar {
width: 6px;
}
.kanban-items::-webkit-scrollbar-thumb {
background-color: #bdc3c7;
border-radius: 3px;
}
.task-card {
background: white;
padding: 12px;
border-radius: 6px;
border-left: 4px solid #3498db;
font-size: 0.85em;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
line-height: 1.4;
}
.task-card.completed {
border-left-color: #2ecc71;
background-color: #fcfcfc;
color: #7f8c8d;
}
.task-card strong {
color: #2c3e50;
display: block;
margin-bottom: 6px;
font-size: 1.05em;
}
.activity-panel {
height: 180px;
margin-top: 20px;
}
#activity-feed {
overflow-y: auto;
flex: 1;
font-family: 'Consolas', 'Courier New', monospace;
font-size: 0.85em;
background: #1e272e;
color: #4cd137;
padding: 15px;
border-radius: 6px;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.2);
}
.feed-entry {
margin-bottom: 6px;
line-height: 1.4;
}
.feed-entry .time {
color: #7f8c8d;
margin-right: 8px;
}
</style> </style>
</head> </head>
<body> <body>
<div class="header-bar"> <div class="header-bar">
<h1>🧠 Antigravity Memory Hub</h1> <h1>🧠 Antigravity Memory Hub</h1>
<div><span class="status-dot"></span>System Online</div> <div style="font-weight: 500;"><span class="status-dot"></span>System Online</div>
</div> </div>
<div class="main-container"> <div class="main-container">
@@ -62,7 +247,9 @@
<button onclick="resetGraph()" title="Reset View">⌂</button> <button onclick="resetGraph()" title="Reset View">⌂</button>
</div> </div>
<div id="network-container"></div> <div id="network-container"></div>
</div><div class="right-column"> </div>
<div class="right-column">
<div class="panel search-panel"> <div class="panel search-panel">
<h2>Omni-Search</h2> <h2>Omni-Search</h2>
<input type="text" id="search-box" placeholder="Search memory (e.g., 'database')..." onkeyup="handleSearch(event)"> <input type="text" id="search-box" placeholder="Search memory (e.g., 'database')..." onkeyup="handleSearch(event)">
@@ -117,13 +304,23 @@
const nodeIds = new Set(); const nodeIds = new Set();
for (const [name, entity] of Object.entries(data.entities)) { for (const [name, entity] of Object.entries(data.entities)) {
newNodes.push({ id: name, label: name, title: entity.observations.join('\n') }); newNodes.push({
id: name,
label: name,
title: entity.observations.join('\n')
});
nodeIds.add(name); nodeIds.add(name);
} }
data.relations.forEach(r => { data.relations.forEach(r => {
if (nodeIds.has(r.from) && nodeIds.has(r.to)) { if (nodeIds.has(r.from) && nodeIds.has(r.to)) {
newEdges.push({ id: r.from + '-' + r.to + '-' + r.relationType, from: r.from, to: r.to, label: r.relationType, arrows: 'to' }); newEdges.push({
id: r.from + '-' + r.to + '-' + r.relationType,
from: r.from,
to: r.to,
label: r.relationType,
arrows: 'to'
});
} }
}); });
@@ -143,13 +340,30 @@
if (!network) { if (!network) {
const container = document.getElementById('network-container'); const container = document.getElementById('network-container');
const options = { const options = {
nodes: { shape: 'dot', size: 16, font: { size: 12, color: '#333' }, color: { background: '#3498db', border: '#2980b9' } }, nodes: {
edges: { color: { color: '#bdc3c7' }, font: { size: 10, align: 'middle' } }, shape: 'dot',
physics: { barnesHut: { gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95 } } size: 16,
font: { size: 12, color: '#333' },
color: { background: '#3498db', border: '#2980b9' },
shadow: true
},
edges: {
color: { color: '#bdc3c7' },
font: { size: 10, align: 'middle' },
smooth: { type: 'continuous' }
},
physics: {
barnesHut: { gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95 }
},
interaction: { hover: true, tooltipDelay: 100 }
}; };
network = new vis.Network(container, { nodes: nodesData, edges: edgesData }, options); network = new vis.Network(container, { nodes: nodesData, edges: edgesData }, options);
} }
} catch (err) { console.error("Failed to load graph", err); document.getElementById("network-container").innerHTML = "<div style=`"color:red;padding:20px;`">Error loading graph: " + err.message + "</div>"; } } } catch (err) {
console.error("Failed to load graph", err);
document.getElementById("network-container").innerHTML = '<div style="color:#e74c3c;padding:20px;font-weight:bold;">Error loading graph: ' + err.message + '</div>';
}
}
// Kanban Board Initialization // Kanban Board Initialization
async function loadTasks() { async function loadTasks() {
@@ -165,7 +379,7 @@
tasks.forEach(t => { tasks.forEach(t => {
const card = document.createElement('div'); const card = document.createElement('div');
card.className = `task-card ${t.status === 'completed' ? 'completed' : ''}`; card.className = `task-card ${t.status === 'completed' ? 'completed' : ''}`;
card.innerHTML = `<strong>${t.title}</strong><br>${t.description}`; card.innerHTML = `<strong>${t.title}</strong>${t.description}`;
if (t.status === 'completed') { if (t.status === 'completed') {
doneContainer.appendChild(card); doneContainer.appendChild(card);
@@ -193,7 +407,7 @@
<div class="search-result-item"> <div class="search-result-item">
<strong>${r.type_name}</strong> <strong>${r.type_name}</strong>
<div>${r.content.substring(0, 100)}...</div> <div>${r.content.substring(0, 100)}...</div>
<div style="font-size:0.8em;color:#95a5a6">Score: ${r.score.toFixed(2)}</div> <div style="font-size:0.85em;color:#95a5a6;margin-top:4px;">Score: ${r.score.toFixed(2)}</div>
</div> </div>
`).join(''); `).join('');
} }
@@ -211,7 +425,7 @@
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'feed-entry'; div.className = 'feed-entry';
const time = new Date().toLocaleTimeString(); const time = new Date().toLocaleTimeString();
div.innerText = `[${time}] ${event.data}`; div.innerHTML = `<span class="time">[${time}]</span> ${event.data}`;
feed.appendChild(div); feed.appendChild(div);
feed.scrollTop = feed.scrollHeight; feed.scrollTop = feed.scrollHeight;
}); });
@@ -229,11 +443,3 @@
</script> </script>
</body> </body>
</html> </html>