<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Graph Tools - Interactive Analysis Toolkit</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Azeret Mono', 'VT323', Helvetica, Arial, sans-serif;
background: #000;
color: #fff;
min-height: 100vh;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 40px;
border-bottom: 3px solid #dcecf5;
padding-bottom: 30px;
}
.header h1 {
font-family: 'VT323', monospace;
font-size: 56px;
margin-bottom: 10px;
font-weight: normal;
letter-spacing: 2px;
}
.header p {
font-size: 18px;
color: #dcecf5;
font-family: 'Azeret Mono', monospace;
}
.main-content {
background: #000;
border: 3px solid #dcecf5;
padding: 40px;
margin-bottom: 30px;
}
.upload-section {
background: #000;
padding: 30px;
margin-bottom: 30px;
border: 3px solid #dcecf5;
}
.upload-section h2 {
font-family: 'VT323', monospace;
font-size: 32px;
margin-bottom: 15px;
font-weight: normal;
}
.upload-area {
text-align: center;
padding: 40px;
border: 3px dashed #dcecf5;
background: #000;
cursor: pointer;
transition: all 0.3s ease;
}
.upload-area:hover {
background: #111;
}
.upload-area.dragover {
border-color: #fff;
background: #111;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-family: 'Azeret Mono', monospace;
font-weight: bold;
margin-bottom: 8px;
color: #dcecf5;
}
.form-row {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.form-row .form-group {
flex: 1;
min-width: 200px;
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 3px solid #dcecf5;
background: #000;
color: #fff;
font-family: 'Azeret Mono', monospace;
font-size: 15px;
transition: border-color 0.3s ease;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #fff;
}
button {
background: #000;
color: #fff;
padding: 15px 25px;
border: 3px solid #dcecf5;
font-family: 'Azeret Mono', monospace;
font-size: 15px;
cursor: pointer;
transition: all 0.3s ease;
margin: 5px;
text-transform: uppercase;
letter-spacing: 1px;
}
button:hover {
background: #dcecf5;
color: #000;
}
button.secondary {
border-color: #dcecf5;
}
button.secondary:hover {
background: #dcecf5;
color: #000;
}
button.sample {
border-color: #dcecf5;
}
button.sample:hover {
background: #dcecf5;
color: #000;
}
.results {
margin-top: 30px;
padding: 20px;
background: #000;
border: 3px solid #dcecf5;
display: none;
}
.results.show {
display: block;
}
.loading {
text-align: center;
padding: 40px;
display: none;
}
.loading.show {
display: block;
}
.spinner {
border: 4px solid #333;
border-top: 4px solid #dcecf5;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.visualization-link {
display: inline-block;
background: #dcecf5;
color: #000;
padding: 15px 30px;
text-decoration: none;
border: 3px solid #dcecf5;
margin: 20px 0;
font-family: 'Azeret Mono', monospace;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: bold;
transition: all 0.3s ease;
}
.visualization-link:hover {
background: #000;
color: #dcecf5;
}
a {
color: #000;
background: #dcecf5;
text-decoration: none;
font-weight: bold;
padding: 2px 4px;
transition: all 0.3s ease;
}
a:hover {
color: #dcecf5;
background: #000;
}
.error {
background: #000;
color: #fff;
padding: 15px;
margin: 10px 0;
border: 3px solid #ff0000;
}
.success {
background: #000;
color: #fff;
padding: 15px;
margin: 10px 0;
border: 3px solid #dcecf5;
}
pre {
background: #000;
color: #dcecf5;
padding: 20px;
border: 3px solid #dcecf5;
overflow-x: auto;
margin: 15px 0;
font-family: 'VT323', monospace;
font-size: 16px;
}
.footer {
text-align: center;
color: #dcecf5;
margin-top: 40px;
border-top: 3px solid #dcecf5;
padding-top: 20px;
font-family: 'Azeret Mono', monospace;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Graph Tools</h1>
<p>Interactive Graph Analysis Toolkit</p>
</div>
<div class="main-content">
<div class="upload-section">
<h2>ANALYZE YOUR GRAPH</h2>
<p style="margin-bottom: 20px; font-family: 'Azeret Mono', monospace;">Upload an adjacency matrix file or try our sample data</p>
<form id="analysisForm" enctype="multipart/form-data">
<div class="upload-area" onclick="document.getElementById('matrixFile').click()">
<div id="uploadText">
<strong>Click to upload matrix file</strong><br>
<span>Supports CSV, JSON, TXT formats</span>
</div>
<input type="file" id="matrixFile" name="matrix" accept=".csv,.json,.txt" style="display: none;" onchange="handleFileSelect(this)">
</div>
<div style="text-align: center; margin-top: 20px;">
<button type="submit">Analyze Graph</button>
<button type="button" class="secondary" onclick="openVisualizer()">Interactive Graph Visualizer</button>
<button type="button" class="secondary" onclick="openMCPDocs()">MCP Server Guide</button>
<br>
<button type="button" class="sample" onclick="generateSample('social')">Social Network Sample</button>
<button type="button" class="sample" onclick="generateSample('dependency')">Dependency Graph Sample</button>
<button type="button" class="sample" onclick="generateSample('basic')">Basic Graph Sample</button>
</div>
</form>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Analyzing your graph...</p>
</div>
<div class="results" id="results">
<h3>Analysis Results</h3>
<div id="resultsContent"></div>
</div>
</div>
<div class="footer">
<p>Graph Tools - Built with Ruby, Node.js, and D3.js</p>
<p>Upload your adjacency matrix and explore your data through interactive visualizations</p>
</div>
</div>
<script>
let selectedFile = null;
function handleFileSelect(input) {
if (input.files.length > 0) {
selectedFile = input.files[0];
document.getElementById('uploadText').innerHTML =
`<strong>${selectedFile.name}</strong><br><span>File selected for analysis</span>`;
}
}
document.getElementById('analysisForm').addEventListener('submit', async (e) => {
e.preventDefault();
if (!selectedFile) {
alert('Please select a matrix file first!');
return;
}
const formData = new FormData();
formData.append('matrix', selectedFile);
showLoading();
try {
const response = await fetch('/api/analyze', {
method: 'POST',
body: formData
});
const result = await response.json();
hideLoading();
if (result.success) {
showResults(result);
} else {
showError(result.error || 'Analysis failed');
}
} catch (error) {
hideLoading();
showError('Network error: ' + error.message);
}
});
async function generateSample(type) {
showLoading();
try {
const response = await fetch('/api/sample', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ type })
});
const result = await response.json();
hideLoading();
if (result.success) {
// Update form with sample data
document.getElementById('uploadText').innerHTML =
`<strong>${type} sample loaded</strong><br><span>Ready for analysis</span>`;
selectedFile = 'sample'; // Flag that we have sample data
showResults(result);
} else {
showError(result.error || 'Sample generation failed');
}
} catch (error) {
hideLoading();
showError('Network error: ' + error.message);
}
}
function showLoading() {
document.getElementById('loading').classList.add('show');
document.getElementById('results').classList.remove('show');
}
function hideLoading() {
document.getElementById('loading').classList.remove('show');
}
function showResults(result) {
const resultsDiv = document.getElementById('results');
const contentDiv = document.getElementById('resultsContent');
let html = '';
if (result.output) {
html += '<div class="success">Analysis completed successfully!</div>';
html += '<pre>' + result.output + '</pre>';
}
if (result.graphData) {
html += `
<div style="margin: 20px 0;">
<h4>Graph Data Summary:</h4>
<p><strong>Nodes:</strong> ${result.graphData.nodes ? result.graphData.nodes.length : 0}</p>
<p><strong>Edges:</strong> ${result.graphData.edges ? result.graphData.edges.length : (result.graphData.links ? result.graphData.links.length : 0)}</p>
</div>
`;
const vizUrl = result.visualizationUrl || '/visualizer';
html += `
<a href="${vizUrl}" target="_blank" class="visualization-link">
Open Interactive Visualization
</a>
`;
}
if (result.visualizationUrl) {
html += `
<div style="margin: 20px 0;">
<p><strong>Auto-loaded!</strong> Your graph data will be automatically displayed in the visualizer</p>
</div>
`;
}
contentDiv.innerHTML = html;
resultsDiv.classList.add('show');
}
function showError(message) {
const resultsDiv = document.getElementById('results');
const contentDiv = document.getElementById('resultsContent');
contentDiv.innerHTML = `<div class="error">Error: ${message}</div>`;
resultsDiv.classList.add('show');
}
function openVisualizer() {
window.open('/visualizer', '_blank');
}
function openMCPDocs() {
window.open('/mcp-documentation.html', '_blank');
}
// Drag and drop functionality
const uploadArea = document.querySelector('.upload-area');
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
selectedFile = files[0];
document.getElementById('uploadText').innerHTML =
`<strong>${selectedFile.name}</strong><br><span>File selected for analysis</span>`;
}
});
</script>
</body>
</html>