<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>sPyTial</title>
<style></style>
<style></style>
<script></script>
<script></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 0;
background: transparent;
height: 100vh;
display: flex;
flex-direction: column;
}
.container {
position: relative;
border: 1px solid #e1e5e9;
border-radius: 6px;
overflow: hidden;
background: white;
flex: 1;
display: flex;
flex-direction: column;
}
.graph-wrapper {
position: relative;
min-height: 400px;
flex: 1;
display: flex;
}
#graph-container {
width: 100%;
height: 100%;
min-height: 400px;
display: block;
flex: 1;
}
.reset-btn {
background: #0366d6;
color: white;
border: none;
padding: 8px 8px;
border-radius: 3px;
font-size: 12px;
cursor: pointer;
transition: background-color 0.15s ease;
line-height: 1.2;
margin-left: 8px;
}
.reset-btn:hover {
background: #0256cc;
}
</style>
</head>
<body>
<div class="container">
<div class="graph-wrapper">
<webcola-cnd-graph
id="graph-container"
width="800"
height="600"
layoutFormat="default"
aria-label="Interactive graph visualization">
</webcola-cnd-graph>
</div>
</div>
<div id="error-message"></div>
<div id="error-core"></div>
<script>
const spytialSpec = `{{ spytial_spec }}`;
const jsonData = `{{ json_data }}`;
let instanceLayout = null;
let graphElement = null;
async function resetLayout() {
if (graphElement && instanceLayout) {
await graphElement.renderLayout(instanceLayout);
}
}
function addResetButton() {
if (graphElement && graphElement.addToolbarControl) {
const resetButton = document.createElement('button');
resetButton.className = 'reset-btn';
resetButton.innerHTML = '↻ Reset';
resetButton.title = 'Reset layout';
resetButton.onclick = resetLayout;
graphElement.addToolbarControl(resetButton);
}
}
async function loadGraph() {
graphElement = document.getElementById('graph-container');
const coreApi = window.CndCore || window.CnDCore || window.spytialcore;
if (!coreApi || typeof coreApi !== 'object') {
console.error('Spytial core library is not available');
showError('Spytial core library failed to load. The visualization cannot be displayed.');
return;
}
if (window.mountErrorMessageModal) {
console.log('Mounting error message modal');
window.mountErrorMessageModal('error-core');
}
try {
let parsedData;
try {
parsedData = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData;
} catch (parseError) {
throw new Error(`Failed to parse JSON data: ${parseError.message}`);
}
console.log(parsedData)
if (typeof coreApi.JSONDataInstance !== 'function') {
throw new Error('Spytial core API mismatch: JSONDataInstance constructor is not available on the global namespace');
}
const dataInstance = new coreApi.JSONDataInstance(parsedData);
window.dataInstance = dataInstance;
const evaluationContext = { sourceData: dataInstance };
const sgqEvaluator = new coreApi.SGraphQueryEvaluator();
sgqEvaluator.initialize(evaluationContext);
console.log('Evaluation context initialized');
window.evaluator = sgqEvaluator;
const layoutSpec = coreApi.parseLayoutSpec(spytialSpec);
console.log('Layout spec parsed');
const layoutInstance = new coreApi.LayoutInstance(layoutSpec, sgqEvaluator, 0, true);
const layoutResult = layoutInstance.generateLayout(dataInstance, {});
console.log('Layout generated');
window.evaluator = sgqEvaluator;
if (layoutResult.error) {
console.error('Layout generation error:', layoutResult.error);
if (layoutResult.error.errorMessages) {
if (window.showPositionalError) {
window.showPositionalError(layoutResult.error.errorMessages);
} else {
showError(`Positional constraint conflict: ${layoutResult.error.message}`);
}
} else if (layoutResult.error.overlappingNodes) {
if (window.showGroupOverlapError) {
window.showGroupOverlapError(layoutResult.error.message);
} else {
showError(`Group overlap error: ${layoutResult.error.message}`);
}
} else {
if (window.showGeneralError) {
window.showGeneralError(`Layout generation error: ${layoutResult.error.message}`);
} else {
showError(`Layout generation error: ${layoutResult.error.message}`);
}
}
const webcolaGraphElement = document.getElementById('graph-container');
webcolaGraphElement.setAttribute('unsat', "");
}
instanceLayout = layoutResult.layout;
const webcolaGraphElement = document.getElementById('graph-container');
await graphElement.renderLayout(instanceLayout);
addResetButton();
} catch (error) {
console.error('Error rendering graph:', error);
if (window.showGeneralError) {
window.showGeneralError(`Layout generation failed: ${error.message}`);
} else {
showError(`Error rendering graph: ${error.message}`);
}
}
}
function showError(message) {
const errorDiv = document.getElementById('error-message');
if (errorDiv) {
errorDiv.innerHTML = `
<div style="color: red; padding: 10px; border: 1px solid red; background-color: #ffe6e6; margin-top: 10px;">
<h3>Error</h3>
<p>${message}</p>
</div>
`;
} else {
console.error('Error div not found. Error message:', message);
}
}
function showInfo(message) {
const errorDiv = document.getElementById('error-message');
if (errorDiv) {
errorDiv.innerHTML = `
<div style="color: #0366d6; padding: 20px; border: 1px solid #0366d6; background-color: #f1f8ff; margin-top: 10px; border-radius: 6px;">
<h3>🦀 Rust Data Structure</h3>
<p>${message}</p>
<details style="margin-top: 15px;">
<summary style="cursor: pointer; font-weight: bold;">Show JSON Data Structure</summary>
<pre style="background: #f6f8fa; padding: 10px; border-radius: 4px; margin-top: 10px; font-size: 12px; max-height: 300px; overflow-y: auto;">${JSON.stringify(JSON.parse(jsonData), null, 2)}</pre>
</details>
</div>
`;
} else {
console.error('Error div not found. Info message:', message);
}
}
window.addEventListener('load', loadGraph);
</script>
</body>
</html>