You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1606 lines
71 KiB
1606 lines
71 KiB
<!DOCTYPE html>
|
|
<html lang="en" class="scroll-smooth">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Codebase Overview - Radar & Video Synchronizer</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
|
|
<!-- Prism.js for Syntax Highlighting (Local Okaidia Theme) -->
|
|
<link href="../vendor/prism.css" rel="stylesheet" />
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;800&family=Roboto+Mono:wght@400;500&display=swap"
|
|
rel="stylesheet">
|
|
<style>
|
|
body {
|
|
font-family: 'Inter', sans-serif;
|
|
background-color: #f8fafc;
|
|
/* slate-50 */
|
|
color: #1e293b;
|
|
/* slate-800 */
|
|
}
|
|
|
|
.font-mono {
|
|
font-family: 'Roboto Mono', monospace;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3 {
|
|
font-weight: 700;
|
|
letter-spacing: -0.025em;
|
|
}
|
|
|
|
/* Card Styling */
|
|
.file-card {
|
|
background-color: white;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 0.75rem;
|
|
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.05);
|
|
transition: all 0.2s ease-in-out;
|
|
padding: 1.25rem;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
cursor: pointer;
|
|
position: relative;
|
|
}
|
|
|
|
.file-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 12px -3px rgba(0, 0, 0, 0.07);
|
|
border-color: #94a3b8;
|
|
}
|
|
|
|
.file-card::after {
|
|
content: "View Code ↗";
|
|
position: absolute;
|
|
top: 1rem;
|
|
right: 1rem;
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
background: #f1f5f9;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.file-card:hover::after {
|
|
opacity: 1;
|
|
}
|
|
|
|
.file-name {
|
|
font-family: 'Roboto Mono', monospace;
|
|
font-weight: 700;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 0.375rem;
|
|
font-size: 0.85rem;
|
|
display: inline-block;
|
|
}
|
|
|
|
.core-badge {
|
|
font-size: 9px;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.025em;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
color: white;
|
|
}
|
|
|
|
.file-desc {
|
|
font-size: 0.9rem;
|
|
color: #475569;
|
|
line-height: 1.5;
|
|
margin-top: 0.75rem;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.file-desc ul {
|
|
list-style-type: none;
|
|
padding-left: 0.25rem;
|
|
margin: 0;
|
|
}
|
|
|
|
.file-desc li {
|
|
position: relative;
|
|
padding-left: 1rem;
|
|
margin-bottom: 0.4rem;
|
|
}
|
|
|
|
.file-desc li::before {
|
|
content: "•";
|
|
position: absolute;
|
|
left: 0;
|
|
font-weight: bold;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.file-link {
|
|
font-size: 0.75rem;
|
|
color: #94a3b8;
|
|
margin-top: 1rem;
|
|
padding-top: 0.75rem;
|
|
border-top: 1px dashed #e2e8f0;
|
|
}
|
|
|
|
/* Sticky Nav Styling */
|
|
.nav-link {
|
|
transition: all 0.2s ease;
|
|
cursor: pointer;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.nav-link:hover {
|
|
background-color: white;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
section {
|
|
scroll-margin-top: 140px;
|
|
}
|
|
|
|
/* Modal Styling */
|
|
#code-modal {
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
/* Modal Source Code Area */
|
|
.code-container {
|
|
background-color: #1e1e1e !important;
|
|
color: #d4d4d4 !important;
|
|
/* VS Code light gray fallback */
|
|
}
|
|
|
|
pre[class*="language-"] {
|
|
margin: 0 !important;
|
|
padding: 1.5rem !important;
|
|
border-radius: 0 !important;
|
|
background: transparent !important;
|
|
font-size: 0.85rem !important;
|
|
line-height: 1.5 !important;
|
|
}
|
|
|
|
code[class*="language-"] {
|
|
font-family: 'Roboto Mono', monospace !important;
|
|
text-shadow: none !important;
|
|
}
|
|
|
|
/* Custom scrollbar */
|
|
.custom-scrollbar::-webkit-scrollbar {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-track {
|
|
background: #1e1e1e;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
background: #333333;
|
|
border: 3px solid #1e1e1e;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
background: #444444;
|
|
}
|
|
|
|
/* --- Interactive Wireframe Styles --- */
|
|
#visual-nav-wrapper {
|
|
position: sticky;
|
|
top: 72px;
|
|
z-index: 40;
|
|
background: rgba(248, 250, 252, 0.95);
|
|
backdrop-filter: blur(8px);
|
|
border-bottom: 1px solid #e2e8f0;
|
|
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
transform-origin: top center;
|
|
/* Default origin for normal state */
|
|
margin-bottom: 2rem;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
|
pointer-events: auto;
|
|
|
|
/* Sizing for centering */
|
|
width: 100%;
|
|
max-width: 100%;
|
|
border-radius: 0.75rem;
|
|
}
|
|
|
|
/* Hide the header text when shrunk to make it cleaner? Optional. */
|
|
#visual-nav-wrapper.shrunk h2 {
|
|
opacity: 0; transition: opacity 0.2s;
|
|
}
|
|
|
|
#visual-nav-wrapper.shrunk:hover h2 {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* The Minimized (Corner) State */
|
|
|
|
#visual-nav-wrapper.shrunk {
|
|
|
|
position: fixed; /* Detach from flow */
|
|
|
|
top: 85px; /* Distance from top */
|
|
|
|
right: 20px; /* Dock to right */
|
|
|
|
left: auto; /* Release left centering */
|
|
|
|
|
|
|
|
width: 900px; /* Ideal width */
|
|
|
|
max-width: 95vw; /* Responsive cap */
|
|
|
|
|
|
|
|
transform: scale(0.3); /* Tiny by default */
|
|
|
|
transform-origin: top right; /* Shrink into corner */
|
|
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
border: 1px solid #cbd5e1;
|
|
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
|
|
border-radius: 1rem;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
/* Hover Interaction for Minimized State */
|
|
/* Only active when the 'interactive' class is added after animation completes */
|
|
#visual-nav-wrapper.shrunk.interactive:hover {
|
|
transform: scale(0.85);
|
|
/* Expand to readable size (not quite full to save space) */
|
|
opacity: 1;
|
|
z-index: 50;
|
|
/* Ensure it pops over everything */
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
}
|
|
|
|
/* The scaling container for the actual grid content */
|
|
#visual-nav-content {
|
|
transition: transform 0.3s ease;
|
|
transform-origin: top center;
|
|
}
|
|
|
|
/* Core Architecture Wrapper Style */
|
|
.core-wrapper {
|
|
position: relative;
|
|
border: 2px solid #d8b4fe;
|
|
/* purple-300 */
|
|
background-color: rgba(250, 245, 255, 0.8);
|
|
/* purple-50 */
|
|
border-radius: 0.75rem;
|
|
padding: 1rem;
|
|
padding-top: 2rem;
|
|
/* Space for label */
|
|
transition: all 0.2s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.core-wrapper:hover {
|
|
border-color: #a855f7;
|
|
/* purple-500 */
|
|
background-color: rgba(250, 245, 255, 1);
|
|
box-shadow: 0 4px 6px -1px rgba(168, 85, 247, 0.1);
|
|
}
|
|
|
|
.core-wrapper::before {
|
|
|
|
content: "Core Architecture (main.js, state.js)";
|
|
|
|
position: absolute;
|
|
|
|
top: 0.5rem;
|
|
|
|
left: 1rem;
|
|
|
|
font-size: 0.7rem;
|
|
|
|
font-weight: 800;
|
|
|
|
text-transform: uppercase;
|
|
|
|
color: #9333ea; /* purple-600 */
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Database/Storage Unit Style - Refined Cylinder */
|
|
|
|
|
|
|
|
.database-unit {
|
|
|
|
|
|
|
|
background: linear-gradient(90deg, #dbeafe 0%, #f0f9ff 50%, #dbeafe 100%);
|
|
|
|
|
|
|
|
border-left: 2px solid #3b82f6;
|
|
|
|
|
|
|
|
border-right: 2px solid #3b82f6;
|
|
|
|
|
|
|
|
border-bottom: 2px solid #3b82f6;
|
|
|
|
|
|
|
|
border-radius: 0 0 20px 20px / 0 0 10px 10px;
|
|
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
margin-top: 12px; /* Space for the top cap */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.database-unit:hover {
|
|
|
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
|
|
|
filter: brightness(1.02);
|
|
|
|
|
|
|
|
box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Top Cap */
|
|
|
|
|
|
|
|
.database-unit::before {
|
|
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
|
|
top: -13px;
|
|
|
|
|
|
|
|
left: -2px;
|
|
|
|
|
|
|
|
right: -2px;
|
|
|
|
|
|
|
|
height: 26px;
|
|
|
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
|
|
|
border: 2px solid #3b82f6;
|
|
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
|
|
box-shadow: inset 0 -2px 5px rgba(59, 130, 246, 0.1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disc Stack Lines */
|
|
|
|
|
|
|
|
.database-unit::after {
|
|
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
|
|
background-image: repeating-linear-gradient(
|
|
|
|
|
|
|
|
180deg,
|
|
|
|
|
|
|
|
transparent,
|
|
|
|
|
|
|
|
transparent 39px,
|
|
|
|
|
|
|
|
rgba(59, 130, 246, 0.2) 40px,
|
|
|
|
|
|
|
|
rgba(59, 130, 246, 0.2) 41px
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.wireframe-box {
|
|
background-color: #f1f5f9;
|
|
/* slate-100 */
|
|
border: 2px dashed #94a3b8;
|
|
/* slate-400 */
|
|
border-radius: 0.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #64748b;
|
|
/* slate-500 */
|
|
font-weight: 600;
|
|
font-size: 0.85rem;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
position: relative;
|
|
text-align: center;
|
|
user-select: none;
|
|
}
|
|
|
|
.wireframe-box:hover {
|
|
background-color: white;
|
|
border-color: #6366f1;
|
|
/* indigo-500 */
|
|
color: #4338ca;
|
|
/* indigo-700 */
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.15);
|
|
z-index: 10;
|
|
}
|
|
|
|
.wireframe-box::after {
|
|
content: "Jump to Code ⬇";
|
|
position: absolute;
|
|
bottom: -20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
font-size: 10px;
|
|
color: #6366f1;
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
white-space: nowrap;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.wireframe-box:hover::after {
|
|
opacity: 1;
|
|
bottom: -24px;
|
|
}
|
|
|
|
/* --- Dark Mode Overrides --- */
|
|
.dark body {
|
|
background-color: #0f172a; /* slate-900 */
|
|
color: #cbd5e1; /* slate-300 */
|
|
}
|
|
.dark header {
|
|
background-color: rgba(15, 23, 42, 0.9); /* slate-900/90 */
|
|
border-bottom-color: #334155; /* slate-700 */
|
|
}
|
|
.dark h1, .dark h2, .dark h3 {
|
|
color: #f1f5f9; /* slate-100 */
|
|
}
|
|
.dark .file-card {
|
|
background-color: #1e293b; /* slate-800 */
|
|
border-color: #475569; /* slate-600 */
|
|
}
|
|
.dark .file-card:hover {
|
|
border-color: #94a3b8; /* slate-400 */
|
|
}
|
|
.dark .file-card::after {
|
|
background-color: #334155; /* slate-700 */
|
|
color: #e2e8f0;
|
|
}
|
|
.dark .file-desc {
|
|
color: #94a3b8; /* slate-400 */
|
|
}
|
|
.dark .file-link {
|
|
color: #64748b; /* slate-500 */
|
|
border-top-color: #334155; /* slate-700 */
|
|
}
|
|
.dark .nav-link:hover {
|
|
background-color: #1e293b; /* slate-800 */
|
|
}
|
|
|
|
.dark .wireframe-box {
|
|
background-color: #1e293b; /* slate-800 */
|
|
border-color: #475569; /* slate-600 */
|
|
color: #94a3b8; /* slate-400 */
|
|
}
|
|
.dark .wireframe-box:hover {
|
|
background-color: #334155;
|
|
}
|
|
|
|
/* Color-coded elements overrides */
|
|
.dark .bg-purple-50 { background-color: rgba(88, 28, 135, 0.2); }
|
|
.dark .bg-purple-100 { background-color: rgba(107, 33, 168, 0.4); }
|
|
.dark .text-purple-700 { color: #d8b4fe; }
|
|
.dark .text-purple-900 { color: #e9d5ff; }
|
|
.dark .border-purple-200 { border-color: #581c87; }
|
|
|
|
.dark .bg-blue-50 { background-color: rgba(30, 58, 138, 0.2); }
|
|
.dark .bg-blue-100 { background-color: rgba(30, 64, 175, 0.4); }
|
|
.dark .text-blue-700 { color: #93c5fd; }
|
|
.dark .text-blue-900 { color: #bfdbfe; }
|
|
.dark .border-blue-200 { border-color: #1e3a8a; }
|
|
|
|
.dark .bg-red-50 { background-color: rgba(127, 29, 29, 0.2); }
|
|
.dark .bg-red-100 { background-color: rgba(153, 27, 27, 0.4); }
|
|
.dark .text-red-700 { color: #fca5a5; }
|
|
.dark .text-red-900 { color: #fecaca; }
|
|
.dark .border-red-200 { border-color: #7f1d1d; }
|
|
|
|
.dark .bg-emerald-50 { background-color: rgba(6, 78, 59, 0.2); }
|
|
.dark .bg-emerald-100 { background-color: rgba(6, 95, 70, 0.4); }
|
|
.dark .text-emerald-700 { color: #6ee7b7; }
|
|
.dark .text-emerald-800 { color: #6ee7b7; }
|
|
.dark .text-emerald-900 { color: #a7f3d0; }
|
|
.dark .border-emerald-200 { border-color: #064e3b; }
|
|
|
|
.dark .bg-amber-50 { background-color: rgba(120, 53, 15, 0.2); }
|
|
.dark .bg-amber-100 { background-color: rgba(146, 64, 14, 0.4); }
|
|
.dark .text-amber-700 { color: #fcd34d; }
|
|
.dark .text-amber-900 { color: #fde68a; }
|
|
.dark .border-amber-200 { border-color: #78350f; }
|
|
|
|
.dark #visual-nav-wrapper {
|
|
background: rgba(15, 23, 42, 0.95);
|
|
border-bottom-color: #334155;
|
|
}
|
|
.dark .core-wrapper {
|
|
background-color: rgba(88, 28, 135, 0.1);
|
|
border-color: #6b21a8;
|
|
}
|
|
.dark .database-unit {
|
|
background: linear-gradient(90deg, #1e3a8a 0%, #172554 50%, #1e3a8a 100%);
|
|
border-color: #1d4ed8;
|
|
}
|
|
.dark .database-unit::before {
|
|
background: #172554;
|
|
border-color: #1d4ed8;
|
|
}
|
|
.dark #file-structure-panel .bg-white {
|
|
background-color: #1e293b;
|
|
border-color: #475569;
|
|
}
|
|
.dark .file-tree .file { color: #94a3b8; }
|
|
.dark .file-tree .folder { color: #e2e8f0; }
|
|
|
|
/* Enhanced Tree View Connectors */
|
|
.file-tree ul {
|
|
border-left: 1px solid #cbd5e1; /* slate-300 */
|
|
margin-left: 0.35rem;
|
|
padding-left: 1rem;
|
|
margin-top: 0.25rem;
|
|
margin-bottom: 0.25rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem; /* space-y-1.5 equivalent */
|
|
}
|
|
.dark .file-tree ul {
|
|
border-color: #475569;
|
|
}
|
|
|
|
/* Position relative for the pseudo-element */
|
|
.file-tree ul li {
|
|
position: relative;
|
|
}
|
|
|
|
/* Horizontal connector line */
|
|
.file-tree ul li::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0.6em; /* Vertically center */
|
|
left: -1rem;
|
|
width: 0.8rem;
|
|
height: 1px;
|
|
background-color: #cbd5e1;
|
|
}
|
|
|
|
.dark .file-tree ul li::before {
|
|
background-color: #475569;
|
|
}
|
|
|
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
|
.no-scrollbar::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
/* Hide scrollbar for IE, Edge and Firefox */
|
|
.no-scrollbar {
|
|
-ms-overflow-style: none; /* IE and Edge */
|
|
scrollbar-width: none; /* Firefox */
|
|
}
|
|
|
|
</style>
|
|
<script>
|
|
tailwind.config = {
|
|
darkMode: "class",
|
|
};
|
|
|
|
function applyTheme(theme) {
|
|
if (theme === 'dark') {
|
|
document.documentElement.classList.add('dark');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
}
|
|
}
|
|
|
|
const savedTheme = localStorage.getItem('color-theme') || 'light';
|
|
applyTheme(savedTheme);
|
|
|
|
window.addEventListener('message', (event) => {
|
|
if (event.data && event.data.type === 'theme-change') {
|
|
applyTheme(event.data.theme);
|
|
}
|
|
});
|
|
</script>
|
|
</head>
|
|
|
|
<body class="antialiased pb-20">
|
|
<!-- Header -->
|
|
<header class="bg-white/90 backdrop-blur-md sticky top-0 z-50 border-b border-slate-200 shadow-sm py-4">
|
|
<div class="w-full px-6 lg:px-8">
|
|
<div class="flex flex-col xl:flex-row items-center justify-between gap-4">
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-3xl">🛠️</span>
|
|
<div>
|
|
<h1 class="text-xl md:text-2xl text-slate-800 m-0 leading-tight">Codebase Overview</h1>
|
|
<p class="text-xs text-slate-500 hidden md:block">Interactive Architecture Map</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Navigation -->
|
|
<nav
|
|
class="flex flex-wrap justify-center gap-2 md:gap-3 text-[11px] md:text-xs font-bold uppercase tracking-wider">
|
|
<a href="#architecture"
|
|
class="nav-link flex items-center gap-2 px-3 py-2 rounded-lg bg-purple-50 text-purple-700 border-purple-200 hover:border-purple-300">
|
|
<span class="w-2 h-2 bg-purple-500 rounded-full"></span> Architecture
|
|
</a>
|
|
<a href="#pipeline"
|
|
class="nav-link flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 text-blue-700 border-blue-200 hover:border-blue-300">
|
|
<span class="w-2 h-2 bg-blue-500 rounded-full"></span> Data Pipeline & Storage
|
|
</a>
|
|
<a href="#sync"
|
|
class="nav-link flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 text-red-700 border-red-200 hover:border-red-300">
|
|
<span class="w-2 h-2 bg-red-500 rounded-full"></span> Sync
|
|
</a>
|
|
<a href="#visualization"
|
|
class="nav-link flex items-center gap-2 px-3 py-2 rounded-lg bg-emerald-50 text-emerald-700 border-emerald-200 hover:border-emerald-300">
|
|
<span class="w-2 h-2 bg-emerald-500 rounded-full"></span> Visualization
|
|
</a>
|
|
<a href="#ui"
|
|
class="nav-link flex items-center gap-2 px-3 py-2 rounded-lg bg-amber-50 text-amber-700 border-amber-200 hover:border-amber-300">
|
|
<span class="w-2 h-2 bg-amber-500 rounded-full"></span> UI & Logic
|
|
</a>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="w-full px-6 lg:px-8 space-y-12 mt-8">
|
|
|
|
|
|
|
|
<!-- INTERACTIVE VISUAL MAP -->
|
|
|
|
<!-- Top Area: Visual Map + File Structure -->
|
|
<div class="flex flex-col lg:flex-row gap-8 mb-12 items-start relative">
|
|
|
|
<!-- LEFT: INTERACTIVE VISUAL MAP -->
|
|
<div id="visual-nav-placeholder" class="flex-grow w-full lg:w-auto transition-all duration-500">
|
|
<div id="visual-nav-wrapper" class="rounded-xl p-4 md:p-6 mb-8">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-sm font-bold text-slate-400 uppercase tracking-widest m-0">Visual Navigation Map</h2>
|
|
<span class="text-xs text-slate-400 italic">Click a UI element to see its code</span>
|
|
</div>
|
|
|
|
<div id="visual-nav-content" class="w-full">
|
|
<!-- Core Architecture Wrapper -->
|
|
<div class="core-wrapper w-full" onclick="scrollToSection('architecture')">
|
|
<!-- Main Grid Layout (Matches App Interface) -->
|
|
<div class="flex gap-4 h-[625px] w-full">
|
|
|
|
<!-- Left: App Ecosystem Rectangle -->
|
|
<div class="flex-grow flex flex-col gap-4">
|
|
|
|
<!-- Top Row: Main UI Components -->
|
|
<div class="flex-grow grid grid-cols-11 gap-4 min-h-0">
|
|
<!-- 1. Data Explorer & Sidebar -->
|
|
<div class="col-span-12 md:col-span-2 h-full">
|
|
<div class="wireframe-box h-full bg-amber-50/50 border-amber-200 text-amber-600 flex flex-col justify-center text-center p-2"
|
|
onclick="event.stopPropagation(); scrollToSection('ui')"
|
|
title="src/dataExplorer.js, src/ui.js">
|
|
Data Explorer & Sidebar
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. Radar Canvas (Center) -->
|
|
<div class="col-span-12 md:col-span-6 h-full">
|
|
<div class="wireframe-box h-full bg-emerald-50/50 border-emerald-200 text-emerald-600 flex flex-col justify-center"
|
|
onclick="event.stopPropagation(); scrollToSection('visualization')"
|
|
title="src/p5/radarSketch.js">
|
|
Radar Visualization Canvas<br>
|
|
<span class="text-xs font-normal opacity-75 mt-1">(p5.js Render Loop)</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. Video & Graph (Right) -->
|
|
<div class="col-span-12 md:col-span-3 h-full flex flex-col gap-4">
|
|
<div class="wireframe-box h-1/2 bg-red-50/50 border-red-200 text-red-600 flex flex-col justify-center"
|
|
onclick="event.stopPropagation(); scrollToSection('sync')"
|
|
title="src/sync.js">
|
|
Video Player<br>
|
|
<span class="text-xs font-normal opacity-75 mt-1">(Sync Engine)</span>
|
|
</div>
|
|
<div class="wireframe-box h-1/2 bg-emerald-50/50 border-emerald-200 text-emerald-600 flex flex-col justify-center"
|
|
onclick="event.stopPropagation(); scrollToSection('visualization')"
|
|
title="src/p5/speedGraphSketch.js">
|
|
Speed Graph
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom Row: Controls (Fits Ecosystem Width) -->
|
|
<div class="h-14">
|
|
<div class="wireframe-box h-full bg-amber-50/50 border-amber-200 text-amber-600"
|
|
onclick="event.stopPropagation(); scrollToSection('ui')"
|
|
title="src/dom.js & src/ui.js">
|
|
Playback Controls & Timeline
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right: Pipeline/Storage (The "Data Injector" - Full Height) -->
|
|
<div class="w-16 h-full flex flex-col pt-2 relative">
|
|
|
|
<!-- Data Flow Arrows (Precisely Aligned) -->
|
|
<div class="absolute inset-0 -left-6 pointer-events-none z-10">
|
|
<!-- Top Arrow (Video) -->
|
|
<div class="absolute top-[75px] w-6">
|
|
<svg viewBox="0 0 24 12" class="text-blue-500 drop-shadow-sm">
|
|
<path d="M2 6h20 M2 6l4-4 M2 6l4 4 M22 6l-4-4 M22 6l-4 4"
|
|
fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
</div>
|
|
<!-- Mid Arrow (Graph) -->
|
|
<div class="absolute top-[235px] w-6">
|
|
<svg viewBox="0 0 24 12" class="text-blue-500 drop-shadow-sm">
|
|
<path d="M2 6h20 M2 6l4-4 M2 6l4 4 M22 6l-4-4 M22 6l-4 4"
|
|
fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
</div>
|
|
<!-- Low Arrow (Controls) -->
|
|
<div class="absolute top-[350px] w-6">
|
|
<svg viewBox="0 0 24 12" class="text-blue-500 drop-shadow-sm">
|
|
<path d="M2 6h20 M2 6l4-4 M2 6l4 4 M22 6l-4-4 M22 6l-4 4"
|
|
fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="database-unit h-full flex flex-col justify-center"
|
|
onclick="event.stopPropagation(); scrollToSection('pipeline')"
|
|
title="src/fileLoader.js, src/db.js">
|
|
<div style="writing-mode: vertical-rl; text-orientation: mixed;" class="flex flex-col items-center justify-center h-full py-4 gap-1">
|
|
<span class="font-bold uppercase tracking-widest text-blue-700 text-[10px] whitespace-nowrap">Data Pipeline & Storage</span>
|
|
<span class="text-[9px] font-medium text-blue-500 opacity-80 tracking-wide">(Loader/DB)</span>
|
|
</div>
|
|
</div> <!-- Small shadow for the base -->
|
|
<div class="h-2 w-full bg-slate-300 rounded-[50%] blur-[2px] mt-1 opacity-40"></div>
|
|
</div>
|
|
|
|
</div>
|
|
</div> <!-- End Core Wrapper -->
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RIGHT: FILE STRUCTURE PANEL (Disappears on scroll) -->
|
|
<div id="file-structure-panel" class="w-full lg:w-1/5 h-full pt-4 transition-opacity duration-300 overflow-y-auto no-scrollbar" style="max-height: 80vh;">
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-5 pl-10 sticky top-0">
|
|
<h3 class="text-xs font-bold text-slate-400 uppercase tracking-widest mb-4 border-b pb-2 flex justify-between">
|
|
<span>Project Files</span>
|
|
<span class="text-[10px] opacity-60">steps</span>
|
|
</h3>
|
|
|
|
<ul class="file-tree text-xs space-y-1.5 font-mono leading-tight">
|
|
<li class="folder text-slate-800 font-bold">steps/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-200 ml-1 mt-1 space-y-1.5">
|
|
<!-- Root Files -->
|
|
<li class="file opacity-70">index.html</li>
|
|
<li class="file opacity-50">Visualization_Start.bat</li>
|
|
<li class="file opacity-50">python_check.bat</li>
|
|
<li class="file opacity-50">package-lock.json</li>
|
|
|
|
<!-- SRC Folder -->
|
|
<li class="folder text-slate-800 font-bold mt-3">src/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
|
|
<!-- Core (Purple) -->
|
|
<li class="file group cursor-help" title="Application Entry Point" onclick="scrollToSection('architecture')"><span class="bg-purple-100 text-purple-700 font-medium px-1.5 py-0.5 rounded border border-purple-200 group-hover:bg-purple-200 transition-colors">main.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('architecture')"><span class="bg-purple-50 text-purple-600 px-1.5 py-0.5 rounded group-hover:bg-purple-100 transition-colors">state.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('architecture')"><span class="bg-purple-50 text-purple-600 px-1.5 py-0.5 rounded group-hover:bg-purple-100 transition-colors">constants.js</span></li>
|
|
|
|
<!-- Pipeline (Blue) -->
|
|
<li class="file group cursor-help mt-1" title="File Input Handler" onclick="scrollToSection('pipeline')"><span class="bg-blue-100 text-blue-700 font-medium px-1.5 py-0.5 rounded border border-blue-200">fileLoader.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('pipeline')"><span class="bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded">fileParsers.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('pipeline')"><span class="bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded">parser.worker.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('pipeline')"><span class="bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded">db.js</span></li>
|
|
|
|
<!-- Sync (Red) -->
|
|
<li class="file group cursor-help mt-1" title="Master Sync Engine" onclick="scrollToSection('sync')"><span class="bg-red-100 text-red-700 font-medium px-1.5 py-0.5 rounded border border-red-200">sync.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('sync')"><span class="bg-red-50 text-red-700 px-1.5 py-0.5 rounded">utils.js</span></li>
|
|
|
|
<!-- UI (Amber) -->
|
|
<li class="file group cursor-help mt-1" title="DOM Reference Cache" onclick="scrollToSection('ui')"><span class="bg-amber-100 text-amber-700 font-medium px-1.5 py-0.5 rounded border border-amber-200">dom.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">ui.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">keyboard.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">dataExplorer.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">session.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">theme.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">modal.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('ui')"><span class="bg-amber-50 text-amber-700 px-1.5 py-0.5 rounded">debug.js</span></li>
|
|
|
|
<!-- Viz (Emerald) -->
|
|
<li class="file group cursor-help mt-1" title="Core Drawing Library" onclick="scrollToSection('visualization')"><span class="bg-emerald-100 text-emerald-800 font-medium px-1.5 py-0.5 rounded border border-emerald-200">drawUtils.js</span></li>
|
|
|
|
<li class="folder text-slate-700 font-medium mt-1">p5/
|
|
<ul class="file-tree pl-3 border-l-2 border-emerald-100 ml-1 mt-1 space-y-1">
|
|
<li class="file group cursor-help" title="Main Radar Renderer" onclick="scrollToSection('visualization')"><span class="bg-emerald-100 text-emerald-800 font-medium px-1.5 py-0.5 rounded border border-emerald-200">radarSketch.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('visualization')"><span class="bg-emerald-50 text-emerald-700 px-1.5 py-0.5 rounded">speedGraphSketch.js</span></li>
|
|
<li class="file group cursor-help" onclick="scrollToSection('visualization')"><span class="bg-emerald-50 text-emerald-700 px-1.5 py-0.5 rounded">zoomSketch.js</span></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<!-- Annex Folder -->
|
|
<li class="folder text-slate-700 font-medium mt-3">annex/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
<li class="file opacity-70">User_Manual.html</li>
|
|
<li class="file opacity-70">code-base-overview.html</li>
|
|
<li class="file opacity-70">Changelog.html</li>
|
|
<li class="file opacity-70">shortcuts.html</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<!-- Intel Folder -->
|
|
<li class="folder text-slate-700 font-medium mt-3">intel/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
<li class="file opacity-70">readme.md</li>
|
|
<li class="file opacity-50">context.md</li>
|
|
<li class="file opacity-50">GEMINI.md</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<!-- Vendor Folder -->
|
|
<li class="folder text-slate-700 font-medium mt-3">vendor/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
<li class="file opacity-50">ag-grid-community.min.js</li>
|
|
<li class="file opacity-50">chart.min.js</li>
|
|
<li class="file opacity-50">clarinet.min.js</li>
|
|
<li class="file opacity-50">p5.js</li>
|
|
<li class="file opacity-50">tailwind-cdn.js</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<!-- Tests Folder -->
|
|
<li class="folder text-slate-700 font-medium mt-3">tests/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
<li class="file opacity-50">test-runner.html</li>
|
|
<li class="file opacity-50">fileLoader.test.js</li>
|
|
<li class="file opacity-50">utils.test.js</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<!-- Data Structures -->
|
|
<li class="folder text-slate-700 font-medium mt-3">Data_structs/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
<li class="file opacity-50">JSON_Structure.json</li>
|
|
<li class="file opacity-50">ROS2_Data_Structure.json</li>
|
|
</ul>
|
|
</li>
|
|
|
|
<!-- Logs -->
|
|
<li class="folder text-slate-700 font-medium mt-3">Console_logs/
|
|
<ul class="file-tree pl-3 border-l-2 border-slate-100 ml-1 mt-1 space-y-1.5">
|
|
<li class="file opacity-50">...</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- 1. Core Architecture (Purple) -->
|
|
<section id="architecture">
|
|
<div class="border border-purple-200 bg-purple-50/50 rounded-2xl p-6 md:p-8 shadow-sm">
|
|
<div class="flex items-center gap-4 mb-8">
|
|
<span
|
|
class="flex items-center justify-center w-12 h-12 bg-purple-100 text-purple-600 rounded-xl text-2xl shadow-sm">🧠</span>
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-purple-900 m-0">Core Architecture</h2>
|
|
<p class="text-purple-600/80 text-sm font-medium">State management and application
|
|
initialization.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<div class="file-card border-l-4 border-l-purple-500" onclick="viewFile('src/main.js')">
|
|
<div class="flex justify-between items-start">
|
|
<h3><span class="file-name bg-purple-100 text-purple-700">src/main.js</span></h3>
|
|
<span class="core-badge bg-purple-500">Core</span>
|
|
</div>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-purple-500"><strong>Entry Point:</strong> Initializes the
|
|
application sequence.</li>
|
|
<li class="before:text-purple-500"><strong>Orchestrator:</strong> Sets up Theme, DB,
|
|
Session Manager.</li>
|
|
<li class="before:text-purple-500"><strong>Glue Code:</strong> Wires independent modules
|
|
together.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Links to: state.js, dom.js, sync.js</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-purple-500/30" onclick="viewFile('src/state.js')">
|
|
<h3><span class="file-name bg-purple-100 text-purple-700">src/state.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-purple-500"><strong>Source of Truth:</strong> Central
|
|
<code>appState</code> object.</li>
|
|
<li class="before:text-purple-500"><strong>Runtime Variables:</strong> Stores current
|
|
frame, playback status.</li>
|
|
<li class="before:text-purple-500"><strong>Global Settings:</strong> SNR limits, boolean
|
|
toggles.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Imported by: Almost all modules</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-purple-500/30" onclick="viewFile('src/constants.js')">
|
|
<h3><span class="file-name bg-purple-100 text-purple-700">src/constants.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-purple-500"><strong>Configuration:</strong> Immutable system
|
|
constants.</li>
|
|
<li class="before:text-purple-500"><strong>Parameters:</strong> <code>VIDEO_FPS</code>,
|
|
Radar boundaries.</li>
|
|
<li class="before:text-purple-500"><strong>Visuals:</strong> Canvas aspect ratios.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Used by: p5 Sketches, fileParsers.js</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 2. Data Pipeline & Storage (Blue) -->
|
|
<section id="pipeline">
|
|
<div class="border border-blue-200 bg-blue-50/50 rounded-2xl p-6 md:p-8 shadow-sm">
|
|
<div class="flex items-center gap-4 mb-8">
|
|
<span
|
|
class="flex items-center justify-center w-12 h-12 bg-blue-100 text-blue-600 rounded-xl text-2xl shadow-sm">💾</span>
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-blue-900 m-0">Data Pipeline & Storage</h2>
|
|
<p class="text-blue-600/80 text-sm font-medium">Input handling, parsing, and persistent caching.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<div class="file-card border-l-4 border-l-blue-500" onclick="viewFile('src/fileLoader.js')">
|
|
<div class="flex justify-between items-start">
|
|
<h3><span class="file-name bg-blue-100 text-blue-700">src/fileLoader.js</span></h3>
|
|
<span class="core-badge bg-blue-500">Core</span>
|
|
</div>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-blue-500"><strong>Input Handling:</strong> Detects JSON/Video
|
|
from Drag & Drop.</li>
|
|
<li class="before:text-blue-500"><strong>Offset Logic:</strong> Extracts timestamps for
|
|
sync offset.</li>
|
|
<li class="before:text-blue-500"><strong>Control:</strong> Triggers Web Worker parsing.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Triggers: parser.worker.js</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-blue-500/30" onclick="viewFile('src/parser.worker.js')">
|
|
<h3><span class="file-name bg-blue-100 text-blue-700">src/parser.worker.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-blue-500"><strong>Background Thread:</strong> Prevents UI
|
|
freezing.</li>
|
|
<li class="before:text-blue-500"><strong>Streaming:</strong> Uses
|
|
<strong>Clarinet.js</strong> for chunk parsing.</li>
|
|
<li class="before:text-blue-500"><strong>Messaging:</strong> Reports real-time progress.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Dependency: vendor/clarinet.min.js</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-blue-500/30" onclick="viewFile('src/fileParsers.js')">
|
|
<h3><span class="file-name bg-blue-100 text-blue-700">src/fileParsers.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-blue-500"><strong>Post-Processing:</strong> Refines raw JSON
|
|
data.</li>
|
|
<li class="before:text-blue-500"><strong>Calculations:</strong> Computes
|
|
<code>timestampMs</code> & global SNR.</li>
|
|
<li class="before:text-blue-500"><strong>Smoothing:</strong> Pre-calculates inter-frame
|
|
times.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Called by: fileLoader.js</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-blue-500/30" onclick="viewFile('src/db.js')">
|
|
<h3><span class="file-name bg-blue-100 text-blue-700">src/db.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-blue-500"><strong>Wrapper:</strong> Abstraction for
|
|
<strong>IndexedDB</strong>.</li>
|
|
<li class="before:text-blue-500"><strong>Caching:</strong> Stores blobs for instant
|
|
reloads.</li>
|
|
<li class="before:text-blue-500"><strong>Management:</strong> Handles "load fresh" &
|
|
metadata.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Used by: session.js, main.js</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 3. Synchronization Engine (Red) -->
|
|
<section id="sync">
|
|
<div class="border border-red-200 bg-red-50/50 rounded-2xl p-6 md:p-8 shadow-sm">
|
|
<div class="flex items-center gap-4 mb-8">
|
|
<span
|
|
class="flex items-center justify-center w-12 h-12 bg-red-100 text-red-600 rounded-xl text-2xl shadow-sm">⏱️</span>
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-red-900 m-0">Synchronization Engine</h2>
|
|
<p class="text-red-600/80 text-sm font-medium">Time-critical logic, playback loops, and drift
|
|
correction.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div class="file-card border-l-4 border-l-red-500" onclick="viewFile('src/sync.js')">
|
|
<div class="flex justify-between items-start">
|
|
<h3><span class="file-name bg-red-100 text-red-700">src/sync.js</span></h3>
|
|
<span class="core-badge bg-red-500">Core</span>
|
|
</div>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-red-500"><strong>Master Clock:</strong> Uses
|
|
<code>performance.now()</code>.</li>
|
|
<li class="before:text-red-500"><strong>Loop:</strong> Custom animation loop decoupled
|
|
from video.</li>
|
|
<li class="before:text-red-500"><strong>Drift Correction:</strong> Forces seek if drift
|
|
> 150ms.</li>
|
|
<li class="before:text-red-500"><strong>Mapping:</strong> Finds exact radar frame for
|
|
video time.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Controls: Video Element, P5 Loop</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-red-500/30" onclick="viewFile('src/utils.js')">
|
|
<h3><span class="file-name bg-red-100 text-red-700">src/utils.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-red-500"><strong>Binary Search:</strong> O(log n) frame lookups.
|
|
</li>
|
|
<li class="before:text-red-500"><strong>Regex:</strong> Extract timestamps from
|
|
filenames.</li>
|
|
<li class="before:text-red-500"><strong>Formatting:</strong> Time display helpers
|
|
(MM:SS.ms).</li>
|
|
</ul>
|
|
</div>
|
|
<div class="file-link">Used by: sync.js, fileLoader.js</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 4. Visualization (Emerald) -->
|
|
<section id="visualization">
|
|
<div class="border border-emerald-200 bg-emerald-50/50 rounded-2xl p-6 md:p-8 shadow-sm">
|
|
<div class="flex items-center gap-4 mb-8">
|
|
<span
|
|
class="flex items-center justify-center w-12 h-12 bg-emerald-100 text-emerald-600 rounded-xl text-2xl shadow-sm">🎨</span>
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-emerald-900 m-0">Visualization</h2>
|
|
<p class="text-emerald-600/80 text-sm font-medium">P5.js sketches and drawing primitives.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<div class="file-card border-l-4 border-l-emerald-500" onclick="viewFile('src/p5/radarSketch.js')">
|
|
<div class="flex justify-between items-start">
|
|
<h3><span class="file-name bg-emerald-100 text-emerald-700">src/p5/radarSketch.js</span>
|
|
</h3>
|
|
<span class="core-badge bg-emerald-500">Core</span>
|
|
</div>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-emerald-500"><strong>Main Canvas:</strong> Primary renderer.</li>
|
|
<li class="before:text-emerald-500"><strong>Scene:</strong> Draws Ego, points, tracks.
|
|
</li>
|
|
<li class="before:text-emerald-500"><strong>Scaling:</strong> World-to-Screen mapping.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-emerald-500" onclick="viewFile('src/drawUtils.js')">
|
|
<div class="flex justify-between items-start">
|
|
<h3><span class="file-name bg-emerald-100 text-emerald-700">src/drawUtils.js</span></h3>
|
|
<span class="core-badge bg-emerald-500">Core</span>
|
|
</div>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-emerald-500"><strong>Library:</strong> Optimized drawing
|
|
functions.</li>
|
|
<li class="before:text-emerald-500"><strong>Decoupling:</strong> Separates logic from
|
|
render.</li>
|
|
<li class="before:text-emerald-500"><strong>Palettes:</strong> SNR & TTC colors.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-emerald-500/30"
|
|
onclick="viewFile('src/p5/zoomSketch.js')">
|
|
<h3><span class="file-name bg-emerald-100 text-emerald-700">src/p5/zoomSketch.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-emerald-500"><strong>GOD MODE:</strong> Magnified cursor view.
|
|
</li>
|
|
<li class="before:text-emerald-500"><strong>Tracking:</strong> Follows mouse
|
|
coordinates.</li>
|
|
<li class="before:text-emerald-500"><strong>Details:</strong> Tooltips for hovered
|
|
items.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-emerald-500/30"
|
|
onclick="viewFile('src/p5/speedGraphSketch.js')">
|
|
<h3><span class="file-name bg-emerald-100 text-emerald-700">src/p5/speedGraphSketch.js</span>
|
|
</h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-emerald-500"><strong>Graphing:</strong> Bottom-right chart.</li>
|
|
<li class="before:text-emerald-500"><strong>Plotting:</strong> Ego vs. CAN Speed.</li>
|
|
<li class="before:text-emerald-500"><strong>Sync:</strong> Playback indicator.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 5. UI & Interaction (Amber) -->
|
|
<section id="ui">
|
|
<div class="border border-amber-200 bg-amber-50/50 rounded-2xl p-6 md:p-8 shadow-sm">
|
|
<div class="flex items-center gap-4 mb-8">
|
|
<span
|
|
class="flex items-center justify-center w-12 h-12 bg-amber-100 text-amber-600 rounded-xl text-2xl shadow-sm">🖥️</span>
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-amber-900 m-0">UI & Interaction</h2>
|
|
<p class="text-amber-600/80 text-sm font-medium">DOM manipulation and user input.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<div class="file-card border-l-4 border-l-amber-500" onclick="viewFile('src/dom.js')">
|
|
<div class="flex justify-between items-start">
|
|
<h3><span class="file-name bg-amber-100 text-amber-700">src/dom.js</span></h3>
|
|
<span class="core-badge bg-amber-500">Core</span>
|
|
</div>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-amber-500"><strong>Caching:</strong> Stores DOM references.</li>
|
|
<li class="before:text-amber-500"><strong>Access:</strong> Efficient element lookup.
|
|
</li>
|
|
<li class="before:text-amber-500"><strong>Updates:</strong> Overlays & Debug.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-amber-500/30" onclick="viewFile('src/ui.js')">
|
|
<h3><span class="file-name bg-amber-100 text-amber-700">src/ui.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-amber-500"><strong>Management:</strong> High-level UI
|
|
interactions.</li>
|
|
<li class="before:text-amber-500"><strong>Components:</strong> Sidebar, Manual,
|
|
Shortcuts.</li>
|
|
<li class="before:text-amber-500"><strong>Toggles:</strong> Fullscreen switching.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-amber-500/30" onclick="viewFile('src/keyboard.js')">
|
|
<h3><span class="file-name bg-amber-100 text-amber-700">src/keyboard.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-amber-500"><strong>Listener:</strong> Intercepts keydown events.
|
|
</li>
|
|
<li class="before:text-amber-500"><strong>Mapping:</strong> Binds keys to functions.
|
|
</li>
|
|
<li class="before:text-amber-500"><strong>Controller:</strong> Hardware input layer.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-amber-500/30" onclick="viewFile('src/dataExplorer.js')">
|
|
<h3><span class="file-name bg-amber-100 text-amber-700">src/dataExplorer.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-amber-500"><strong>Inspector:</strong> Panel logic (AG Grid).
|
|
</li>
|
|
<li class="before:text-amber-500"><strong>Plot View:</strong> Integrates Chart.js.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-amber-500/30" onclick="viewFile('src/session.js')">
|
|
<h3><span class="file-name bg-amber-100 text-amber-700">src/session.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-amber-500"><strong>Persistence:</strong> Serializes state to
|
|
JSON.</li>
|
|
<li class="before:text-amber-500"><strong>Restoration:</strong> Re-hydrates from files.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="file-card border-l-4 border-l-amber-500/30" onclick="viewFile('src/theme.js')">
|
|
<h3><span class="file-name bg-amber-100 text-amber-700">src/theme.js</span></h3>
|
|
<div class="file-desc">
|
|
<ul>
|
|
<li class="before:text-amber-500"><strong>Theming:</strong> Dark/Light mode toggle.</li>
|
|
<li class="before:text-amber-500"><strong>Storage:</strong> Persists user preference.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Footer -->
|
|
<footer class="text-center mt-12 text-slate-500 text-sm pb-8">
|
|
<p>Generated for the Radar & Video Synchronizer Project.</p>
|
|
</footer>
|
|
</div>
|
|
|
|
<!-- CODE VIEWER MODAL -->
|
|
<div id="code-modal" class="fixed inset-0 z-[100] hidden">
|
|
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" onclick="closeModal()"></div>
|
|
|
|
<div
|
|
class="absolute inset-4 md:inset-10 bg-[#1e1e1e] rounded-xl shadow-2xl flex flex-col overflow-hidden border border-slate-700">
|
|
<div class="flex items-center justify-between px-4 py-3 bg-[#252526] border-b border-black">
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-xl">📄</span>
|
|
<span id="modal-title" class="text-slate-200 font-mono text-sm font-bold">file.js</span>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<button onclick="copyCode()"
|
|
class="px-3 py-1 bg-blue-600 hover:bg-blue-500 text-white text-xs font-bold rounded transition">Copy</button>
|
|
<button onclick="closeModal()"
|
|
class="px-3 py-1 bg-red-600 hover:bg-red-500 text-white text-xs font-bold rounded transition">Close</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex-grow overflow-auto bg-[#1e1e1e] custom-scrollbar code-container">
|
|
<pre id="pre-container"><code>Loading...</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Prism JS Scripts -->
|
|
<script src="../vendor/prism.js"></script>
|
|
|
|
<script>
|
|
const modal = document.getElementById('code-modal');
|
|
const modalTitle = document.getElementById('modal-title');
|
|
const preContainer = document.getElementById('pre-container');
|
|
|
|
async function viewFile(filePath) {
|
|
modal.classList.remove('hidden');
|
|
modalTitle.textContent = filePath;
|
|
|
|
// Initial loading state
|
|
preContainer.innerHTML = `<code class="language-text">Loading ${filePath}...</code>`;
|
|
|
|
let lang = 'javascript';
|
|
if (filePath.endsWith('.json')) lang = 'json';
|
|
else if (filePath.endsWith('.html')) lang = 'markup';
|
|
else if (filePath.endsWith('.css')) lang = 'css';
|
|
|
|
try {
|
|
// Since this file is in 'annex/', we need to go up one level to find the 'src/' files
|
|
const response = await fetch('../' + filePath);
|
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
const text = await response.text();
|
|
|
|
// Create code element with correct class for Prism
|
|
const codeEl = document.createElement('code');
|
|
codeEl.className = `language-${lang}`;
|
|
codeEl.textContent = text;
|
|
|
|
// Update container
|
|
preContainer.innerHTML = '';
|
|
preContainer.className = `language-${lang}`;
|
|
preContainer.appendChild(codeEl);
|
|
|
|
// Trigger Highlight
|
|
if (window.Prism) {
|
|
Prism.highlightElement(codeEl);
|
|
}
|
|
|
|
console.groupCollapsed(`📄 Source: ${filePath}`);
|
|
console.log(text);
|
|
console.groupEnd();
|
|
|
|
} catch (err) {
|
|
const isLocal = window.location.protocol === 'file:';
|
|
let errorMsg = `<div class="p-4 text-red-400 font-mono text-sm">`;
|
|
errorMsg += `<strong>Error loading file:</strong> ${err.message}<br><br>`;
|
|
|
|
if (isLocal) {
|
|
errorMsg += `<em>It seems you are opening this HTML file directly (file:// protocol).<br>`;
|
|
errorMsg += `Browsers block AJAX requests to local files for security.<br>`;
|
|
errorMsg += `Please use the "Codebase Overview" button in the main app (served via localhost) or run a local server.</em><br><br>`;
|
|
errorMsg += `<a href="${filePath}" target="_blank" class="underline text-blue-400">Click here to open ${filePath} in a new tab</a>`;
|
|
}
|
|
|
|
errorMsg += `</div>`;
|
|
preContainer.innerHTML = errorMsg;
|
|
console.error("Failed to load file:", err);
|
|
}
|
|
}
|
|
|
|
function closeModal() {
|
|
modal.classList.add('hidden');
|
|
}
|
|
|
|
function copyCode() {
|
|
const codeEl = preContainer.querySelector('code');
|
|
if (codeEl) {
|
|
navigator.clipboard.writeText(codeEl.textContent);
|
|
const btn = document.querySelector('button[onclick="copyCode()"]');
|
|
const originalText = btn.textContent;
|
|
btn.textContent = "Copied!";
|
|
setTimeout(() => btn.textContent = originalText, 1500);
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === "Escape") closeModal();
|
|
});
|
|
|
|
// --- Visual Map Logic ---
|
|
function scrollToSection(id) {
|
|
const el = document.getElementById(id);
|
|
if (el) {
|
|
// Offset for sticky header
|
|
const headerOffset = 180; // approximate shrunk header height
|
|
const elementPosition = el.getBoundingClientRect().top;
|
|
const offsetPosition = elementPosition + window.scrollY - headerOffset;
|
|
|
|
window.scrollTo({
|
|
top: offsetPosition,
|
|
behavior: "smooth"
|
|
});
|
|
|
|
// Highlight effect
|
|
el.classList.add('ring-2', 'ring-blue-500', 'ring-offset-2');
|
|
setTimeout(() => el.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-2'), 2000);
|
|
}
|
|
}
|
|
|
|
// Shrink-on-scroll effect
|
|
|
|
const navWrapper = document.getElementById('visual-nav-wrapper');
|
|
|
|
const navPlaceholder = document.getElementById('visual-nav-placeholder');
|
|
|
|
const filePanel = document.getElementById('file-structure-panel');
|
|
|
|
let interactTimeout = null;
|
|
|
|
|
|
|
|
window.addEventListener('scroll', () => {
|
|
|
|
const currentScrollY = window.scrollY;
|
|
|
|
|
|
|
|
// Threshold: When the user scrolls past the initial header + some buffer
|
|
|
|
if (currentScrollY > 100) {
|
|
|
|
if (!navWrapper.classList.contains('shrunk')) {
|
|
|
|
// Lock the height of the placeholder to prevent layout shift
|
|
|
|
navPlaceholder.style.height = navWrapper.offsetHeight + 'px';
|
|
|
|
|
|
|
|
navWrapper.classList.add('shrunk');
|
|
|
|
navWrapper.title = "Hover to expand map";
|
|
|
|
|
|
|
|
// Enable hover interaction ONLY after animation completes (500ms)
|
|
|
|
if (interactTimeout) clearTimeout(interactTimeout);
|
|
|
|
interactTimeout = setTimeout(() => {
|
|
|
|
navWrapper.classList.add('interactive');
|
|
|
|
}, 500);
|
|
|
|
|
|
|
|
// Hide File Structure (Only on Desktop where it sits alongside)
|
|
|
|
if (filePanel && window.innerWidth >= 1024) {
|
|
|
|
filePanel.style.opacity = '0';
|
|
|
|
filePanel.style.pointerEvents = 'none';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (navWrapper.classList.contains('shrunk')) {
|
|
|
|
// Reset interaction state immediately
|
|
|
|
navWrapper.classList.remove('shrunk', 'interactive');
|
|
|
|
if (interactTimeout) clearTimeout(interactTimeout);
|
|
|
|
|
|
|
|
navWrapper.title = "";
|
|
|
|
|
|
|
|
// Unlock the height
|
|
|
|
navPlaceholder.style.height = 'auto';
|
|
|
|
|
|
|
|
// Show File Structure
|
|
|
|
if (filePanel) {
|
|
|
|
filePanel.style.opacity = '1';
|
|
|
|
filePanel.style.pointerEvents = 'auto';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, { passive: true });
|
|
</script></body>
|
|
|
|
</html>
|