excalidraw-rooms/static/editor.html

241 lines
8.2 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Excalidraw Editor</title>
<style>
* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; }
#root { width: 100%; height: 100%; }
.toolbar {
position: fixed;
top: 10px;
left: 10px;
z-index: 1000;
display: flex;
gap: 8px;
background: rgba(255,255,255,0.95);
padding: 8px 12px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.toolbar button, .toolbar a {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
background: #6c5ce7;
color: white;
text-decoration: none;
display: inline-block;
}
.toolbar button:hover, .toolbar a:hover { background: #5b4cdb; }
.toolbar .secondary { background: #ddd; color: #333; }
.toolbar .secondary:hover { background: #ccc; }
.toolbar .title {
font-weight: 600;
padding: 6px 0;
color: #333;
}
.status {
position: fixed;
bottom: 10px;
right: 10px;
padding: 6px 12px;
background: rgba(0,0,0,0.7);
color: white;
border-radius: 4px;
font-size: 12px;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s;
}
.status.show { opacity: 1; }
.loading {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 1.2rem;
color: #666;
flex-direction: column;
gap: 1rem;
}
.error { color: #e74c3c; }
</style>
</head>
<body>
<div class="toolbar">
<a href="/" class="secondary">Back to Library</a>
<span class="title" id="title">Loading...</span>
<button onclick="saveDrawing()">Save</button>
</div>
<div id="root">
<div class="loading">
<div>Loading Excalidraw...</div>
<div id="load-status" style="font-size: 0.9rem; color: #888;"></div>
</div>
</div>
<div id="status" class="status">Saved</div>
<script>
// Get drawing ID from URL path: /draw/{id}
var pathParts = window.location.pathname.split('/');
var drawingId = pathParts[pathParts.length - 1] || pathParts[pathParts.length - 2];
if (!drawingId) {
document.getElementById('root').innerHTML = '<div class="loading error">No drawing ID specified</div>';
throw new Error('No drawing ID');
}
document.getElementById('title').textContent = drawingId;
document.title = drawingId + ' - Excalidraw';
var excalidrawAPI = null;
var autoSaveTimeout = null;
function updateLoadStatus(msg) {
var el = document.getElementById('load-status');
if (el) el.textContent = msg;
console.log('[Excalidraw]', msg);
}
function showStatus(msg) {
var el = document.getElementById('status');
el.textContent = msg;
el.classList.add('show');
setTimeout(function() { el.classList.remove('show'); }, 2000);
}
async function loadDrawing() {
try {
var resp = await fetch('/api/drawings/' + drawingId);
if (resp.ok) {
return await resp.json();
}
} catch (e) {
console.log('No existing drawing, starting fresh');
}
return null;
}
async function saveDrawing() {
if (!excalidrawAPI) {
console.log('Cannot save: excalidrawAPI not ready');
return;
}
var elements = excalidrawAPI.getSceneElements();
var appState = excalidrawAPI.getAppState();
var data = {
type: "excalidraw",
version: 2,
source: "excalidraw-library",
elements: elements,
appState: {
viewBackgroundColor: appState.viewBackgroundColor,
gridSize: appState.gridSize
}
};
try {
await fetch('/api/drawings/' + drawingId, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
showStatus('Saved');
} catch (e) {
showStatus('Save failed!');
console.error('Save error:', e);
}
}
function onChange() {
clearTimeout(autoSaveTimeout);
autoSaveTimeout = setTimeout(saveDrawing, 2000);
}
// Load scripts dynamically
function loadScript(src) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = src;
script.crossOrigin = 'anonymous';
script.onload = resolve;
script.onerror = function() { reject(new Error('Failed to load: ' + src)); };
document.head.appendChild(script);
});
}
async function init() {
try {
updateLoadStatus('Loading React...');
await loadScript('https://unpkg.com/react@18.2.0/umd/react.production.min.js');
updateLoadStatus('Loading ReactDOM...');
await loadScript('https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js');
updateLoadStatus('Loading Excalidraw...');
await loadScript('https://unpkg.com/@excalidraw/excalidraw@0.17.6/dist/excalidraw.production.min.js');
updateLoadStatus('Initializing...');
// Verify libraries loaded
if (!window.React) throw new Error('React not loaded');
if (!window.ReactDOM) throw new Error('ReactDOM not loaded');
if (!window.ExcalidrawLib) throw new Error('ExcalidrawLib not loaded');
console.log('React version:', React.version);
console.log('ExcalidrawLib:', Object.keys(ExcalidrawLib));
updateLoadStatus('Loading drawing data...');
var initialData = await loadDrawing();
updateLoadStatus('Rendering Excalidraw...');
// Create Excalidraw component
function App() {
return React.createElement(ExcalidrawLib.Excalidraw, {
initialData: initialData ? {
elements: initialData.elements || [],
appState: initialData.appState || {}
} : undefined,
excalidrawAPI: function(api) {
excalidrawAPI = api;
console.log('Excalidraw API ready');
},
onChange: onChange
});
}
var root = ReactDOM.createRoot(document.getElementById('root'));
root.render(React.createElement(App));
console.log('Excalidraw rendered successfully');
} catch (e) {
console.error('Init error:', e);
document.getElementById('root').innerHTML =
'<div class="loading error">' +
'<div>Failed to load Excalidraw</div>' +
'<div style="font-size:0.9rem">' + e.message + '</div>' +
'</div>';
}
}
// Keyboard shortcut: Ctrl+S to save
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
saveDrawing();
}
});
init();
</script>
</body>
</html>