getHtmlTemplate function
Implementation
String getHtmlTemplate() => '''
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Riverpod Graph</title>
<script src="https://unpkg.com/cytoscape@3.24.0/dist/cytoscape.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
html, body, #container {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#cy {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
#edge-info {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #ccc;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 10;
display: none;
max-width: 300px;
font-family: sans-serif;
overflow-wrap: break-word; /* Wrap long words */
word-break: break-word; /* Force breaking if needed */
white-space: normal; /* Allow text to wrap */
}
</style>
</head>
<body>
<div id="container">
<div id="cy"></div>
<div id="edge-info"></div>
</div>
<script>
const graphData = {{graph}};
const cy = cytoscape({
container: document.getElementById('cy'),
elements: graphData,
layout: { name: 'breadthfirst' },
style: [
{
selector: 'node',
style: {
'shape': 'roundrectangle',
'label': 'data(label)',
'text-valign': 'center',
'padding': '10px',
'color': '#fff',
'background-color': '#007acc',
'width': 'label',
'height': 'label',
}
},
{
selector: 'edge',
style: {
'width': 2,
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
}
},
{
selector: 'edge.watch',
style: {
'line-color': '#007acc',
'target-arrow-color': '#007acc',
'line-style': 'solid',
}
},
{
selector: 'edge.read',
style: {
'line-color': '#00b894',
'target-arrow-color': '#00b894',
'line-style': 'dashed',
}
},
{
selector: 'edge.listen',
style: {
'line-color': '#0D920F',
'target-arrow-color': '#0D920F',
'line-style': 'dotted',
}
},
{
selector: 'edge.highlighted',
style: {
'background-color': '#ff5e28',
'line-color': '#ff5e28',
'target-arrow-color': '#ff5e28',
'width': 2,
'z-index': 9999,
'transition-property': 'background-color, line-color, target-arrow-color',
'transition-duration': '300ms'
}
},
{
selector: 'edge.suppressed',
style: {
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'width': 1,
'transition-property': 'background-color, line-color, target-arrow-color',
'transition-duration': '300ms'
}
},
{
selector: 'node.highlighted',
style: {
'background-color': '#ff5e28',
'line-color': '#ff5e28',
'target-arrow-color': '#ff5e28',
'z-index': 9999,
'transition-property': 'background-color, line-color, target-arrow-color',
'transition-duration': '300ms'
}
},
{
selector: 'node.suppressed',
style: {
'line-color': '#ccc',
'transition-property': 'background-color, line-color, target-arrow-color',
'transition-duration': '300ms'
}
}
]
});
// Optional: highlight on click
cy.on('tap', 'edge', function (evt) {
const edge = evt.target;
cy.edges().addClass('suppressed');
edge.addClass('highlighted');
edge.removeClass('suppressed');
const type = edge.data('label') || '';
const source = edge.data('source');
const target = edge.data('target');
const trace = edge.data('trace') || '';
const edgeInfoDiv = document.getElementById('edge-info');
edgeInfoDiv.innerHTML = `
<strong>Edge Selected</strong><br>
<strong>From:</strong> \${source}<br>
<strong>To:</strong> \${target}<br>
<strong>Type:</strong> \${type}<br>
<strong>Trace:</strong> <pre style="white-space:pre-wrap">\${trace}</pre>
`;
edgeInfoDiv.style.display = 'block';
});
cy.on('select', 'node', function (evt) {
const node = evt.target;
// Clear previous highlights
cy.elements().removeClass('highlighted');
cy.elements().addClass('suppressed');
node.connectedEdges().addClass('highlighted');
node.connectedEdges().removeClass('suppressed');
// Highlight the node, its connected edges, and neighboring nodes
const neighborhood = node.closedNeighborhood();
neighborhood.addClass('highlighted');
});
cy.on('unselect', 'node', function () {
cy.elements().removeClass('highlighted');
cy.elements().removeClass('suppressed');
});
cy.on('unselect', 'edge', function () {
cy.edges().removeClass('highlighted');
cy.edges().removeClass('suppressed');
const edgeInfoDiv = document.getElementById('edge-info');
edgeInfoDiv.style.display = 'none';
});
</script>
</body>
</html>
''';