mermaid-edit/index-with-auth.php
DJP c4c3e02b41 Initial commit: Interactive Mermaid Editor
Features implemented:
- Visual node/edge editing with property panels
- Drag and drop node positioning
- Color customization for nodes and edges
- Text editing with real-time updates
- Bidirectional Mermaid code synchronization
- Subgraph/grouping support for layout control
- Export functionality to JSON with position data
- Layout lock system (experimental)
- Edge label editing and styling
- Comprehensive Mermaid syntax parser

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 09:14:26 -04:00

257 lines
8.2 KiB
PHP

<?php
require_once 'config.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo SITE_TITLE; ?></title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Montserrat', sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
background-color: #333;
color: #fff;
text-align: center;
padding: 1rem;
}
main {
flex-grow: 1;
padding: 2rem;
display: flex;
flex-direction: column;
}
.container {
display: flex;
gap: 2rem;
flex-grow: 1;
}
.input-area, .output-area {
flex: 1;
display: flex;
flex-direction: column;
}
textarea {
width: 100%;
height: 300px;
margin-bottom: 1rem;
font-family: 'Montserrat', sans-serif;
}
#mermaidOutput {
border: 1px solid #ccc;
padding: 1rem;
flex-grow: 1;
overflow: auto;
}
.button-container {
margin-top: 1rem;
}
button {
padding: 0.5rem 1rem;
background-color: #333;
color: #fff;
border: none;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
}
button:hover {
background-color: #555;
}
footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 1rem;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<!-- auth 1 of 4 -->
<script src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.min.js" crossorigin="anonymous"></script>
<style>
#protected-content {
display: block;
}
</style>
<!-- end auth block -->
</head>
<body>
<header>
<h1><?php echo SITE_TITLE; ?></h1>
</header>
<main>
<!-- auth 2 of 4 -->
<div style="text-align: left;">
<button id="logout-button" onclick="signOut()" style="display:none;">Log Out</button>
<button id="login-button" onclick="signIn()" style="display:none;">Log In</button>
</div>
<!-- end auth block -->
<div class="container">
<div class="input-area">
<h2>Input Mermaid Diagram Code</h2>
<textarea id="mermaidInput">graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Android]
C -->|Two| E[Nuka Cola]
C -->|Three| F[Cold fusion generator]</textarea>
<div class="button-container">
<button id="renderBtn">Render Diagram</button>
</div>
</div>
<div class="output-area" id="protected-content">
<h2>Rendered Diagram</h2>
<div id="mermaidOutput"></div>
<div class="button-container">
<button id="exportBtn">Download as PNG</button>
</div>
</div>
</div>
</main>
<footer>
<p>&copy; <?php echo date('Y'); ?> <?php echo SITE_TITLE; ?></p>
</footer>
<script>
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
primaryColor: '#ffc406',
primaryTextColor: '#000000',
primaryBorderColor: '#000000',
lineColor: '#000000',
secondaryColor: '#B8B9B9',
tertiaryColor: '#B8B9B9'
},
flowchart: {
useMaxWidth: false,
htmlLabels: true,
curve: 'basis'
},
securityLevel: 'loose',
fontFamily: 'Montserrat, sans-serif',
fontSize: 14
});
function renderMermaidDiagram() {
const input = document.getElementById('mermaidInput').value;
const output = document.getElementById('mermaidOutput');
output.innerHTML = ''; // Clear previous content
mermaid.render('mermaid-svg', input).then(result => {
output.innerHTML = result.svg;
}).catch(error => {
output.innerHTML = `<p style="color: red;">Error rendering diagram: ${error.message}</p>`;
});
}
// Initial render
document.addEventListener('DOMContentLoaded', renderMermaidDiagram);
document.getElementById('renderBtn').addEventListener('click', renderMermaidDiagram);
document.getElementById('exportBtn').addEventListener('click', function() {
const svg = document.querySelector('#mermaidOutput svg');
if (!svg) {
alert('No diagram to export. Please render a diagram first.');
return;
}
const svgData = new XMLSerializer().serializeToString(svg);
// Create a high-resolution canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const scale = 4; // Increase this for higher resolution
canvas.width = svg.viewBox.baseVal.width * scale;
canvas.height = svg.viewBox.baseVal.height * scale;
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const pngFile = canvas.toDataURL('image/png');
const downloadLink = document.createElement('a');
downloadLink.download = 'mermaid_diagram_high_res.png';
downloadLink.href = pngFile;
downloadLink.click();
};
img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData)));
});
</script>
<!-- auth 4 of 4 NOTE: ensure values for clientID, authority (URL with tenant ID) and redirectUri are correct below -->
<script>
const msalConfig = {
auth: {
clientId: "9079054c-9620-4757-a256-23413042f1ef",
authority: "https://login.microsoftonline.com/e519c2e6-bc6d-4fdf-8d9c-923c2f002385",
redirectUri: "https://ai-sandbox.oliver.solutions/mermaid"
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true,
}
};
const loginRequest = {
scopes: ["user.read"]
};
const myMSALObj = new msal.PublicClientApplication(msalConfig);
signIn();
function signIn() {
myMSALObj.loginPopup(loginRequest)
.then(loginResponse => {
console.log("User logged in:", loginResponse.account.username);
thisUser = loginResponse.account.username;
sessionStorage.setItem('accessToken', loginResponse.accessToken);
showProtectedContent(); // Show protected content after successful login
//onAuthenticated(); // Special for this app
}).catch(error => {
console.error("Error during login:", error);
});
}
function signOut() {
// Clear the session storage and (does not) sign out from Microsoft Identity
sessionStorage.removeItem('accessToken');
//myMSALObj.logoutPopup();
console.log("User logged out.");
document.getElementById('protected-content').style.display = 'none'; // Hide protected content
document.getElementById('logout-button').style.display = 'none'; // Hide logout button
document.getElementById('login-button').style.display = 'flex'; // Show login button
}
function showProtectedContent() {
// Verify that the access token exists before showing protected content
const accessToken = sessionStorage.getItem('accessToken');
if (accessToken) {
document.getElementById('protected-content').style.display = 'block';
document.getElementById('logout-button').style.display = 'block'; // Show logout button
document.getElementById('login-button').style.display = 'none'; // Hide login button
}
}
// Check if the user is already logged in when the page loads
window.addEventListener('load', showProtectedContent);
</script>
<!-- end auth block -->
</body>
</html>