generated from coulomb/repo-seed
Changes: - Add fileEditor.init() call in DOMContentLoaded to activate edit buttons - Remove individual file upload inputs (projectInput, svgInput, cssInput, csvInput) that had CORS issues when loading project configurations - Keep only the folder picker which works reliably - Update UI to emphasize folder picker as the primary loading method - Remove corresponding event handlers from engine.js - Remove tests for individual file upload functionality The folder picker loads all project files in one operation without CORS issues, while individual file uploads failed when trying to load referenced files (CSV, SVG, CSS) from the project.json. All 56 tests passing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
525 lines
16 KiB
HTML
525 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title>Timeline Generator</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>
|
||
<link id="dynamicCss" rel="stylesheet" href="">
|
||
<style>
|
||
/* File Manager Styling */
|
||
.file-item {
|
||
background: #fff;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 8px;
|
||
padding: 12px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.file-item:hover {
|
||
border-color: #495057;
|
||
box-shadow: 0 2px 8px rgba(73, 80, 87, 0.1);
|
||
}
|
||
|
||
.file-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.file-label {
|
||
font-weight: 600;
|
||
color: #495057;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.upload-btn {
|
||
background: #495057;
|
||
color: white;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s;
|
||
border: none;
|
||
user-select: none;
|
||
}
|
||
|
||
.upload-btn:hover {
|
||
background: #343a40;
|
||
}
|
||
|
||
.edit-btn {
|
||
background: #6c757d;
|
||
color: white;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s;
|
||
border: none;
|
||
user-select: none;
|
||
}
|
||
|
||
.edit-btn:hover:not(:disabled) {
|
||
background: #545b62;
|
||
}
|
||
|
||
.edit-btn:disabled {
|
||
background: #e9ecef;
|
||
color: #adb5bd;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.modified-badge {
|
||
display: inline-block;
|
||
background: #fd7e14;
|
||
color: white;
|
||
font-size: 9px;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
margin-left: 6px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.editor-modal {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.6);
|
||
z-index: 10000;
|
||
padding: 20px;
|
||
overflow: auto;
|
||
}
|
||
|
||
.editor-content {
|
||
background: white;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||
display: flex;
|
||
flex-direction: column;
|
||
max-height: 90vh;
|
||
}
|
||
|
||
.editor-header {
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid #dee2e6;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.editor-body {
|
||
padding: 20px;
|
||
flex: 1;
|
||
overflow: auto;
|
||
}
|
||
|
||
.editor-textarea {
|
||
width: 100%;
|
||
min-height: 400px;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
||
font-size: 13px;
|
||
padding: 12px;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
resize: vertical;
|
||
}
|
||
|
||
.editor-footer {
|
||
padding: 16px 20px;
|
||
border-top: 1px solid #dee2e6;
|
||
display: flex;
|
||
gap: 12px;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.file-status {
|
||
border-top: 1px solid #f1f3f4;
|
||
padding-top: 8px;
|
||
}
|
||
|
||
.file-name {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
display: block;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
background: #f8f9fa;
|
||
color: #6c757d;
|
||
font-style: italic;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.file-name[style*="color: #28a745"] {
|
||
background: #d4edda;
|
||
border: 1px solid #c3e6cb;
|
||
color: #155724 !important;
|
||
font-style: normal;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.file-name[style*="color: #dc3545"] {
|
||
background: #f8d7da;
|
||
border: 1px solid #f1b6bb;
|
||
color: #721c24 !important;
|
||
font-style: normal;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.file-name[style*="color: #6c757d"] {
|
||
background: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
}
|
||
|
||
.controls {
|
||
animation: fadeInUp 0.3s ease;
|
||
}
|
||
|
||
@keyframes fadeInUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
#fileManager {
|
||
transition: all 0.3s ease-in-out;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.file-grid {
|
||
grid-template-columns: 1fr !important;
|
||
}
|
||
|
||
.file-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 6px;
|
||
}
|
||
|
||
.upload-btn {
|
||
align-self: flex-end;
|
||
}
|
||
|
||
.controls {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.controls button {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
/* Button improvements */
|
||
button {
|
||
transition: all 0.2s ease;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
button:hover:not(:disabled) {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||
}
|
||
|
||
button:disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
/* SVG Viewer enhancements */
|
||
#viewerContainer {
|
||
position: relative;
|
||
border: 1px solid #ccd3db;
|
||
background: white;
|
||
border-radius: 8px;
|
||
min-height: 400px;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
#viewer {
|
||
overflow: auto;
|
||
padding: 12px;
|
||
height: 100%;
|
||
max-height: 80vh;
|
||
transform-origin: top left;
|
||
transition: transform 0.2s ease;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
min-width: 100%;
|
||
}
|
||
|
||
#viewer svg {
|
||
max-width: none !important;
|
||
height: auto !important;
|
||
width: auto !important;
|
||
display: block;
|
||
margin: 0;
|
||
min-width: 100%;
|
||
box-sizing: content-box;
|
||
}
|
||
|
||
/* Ensure zoom doesn't get clipped */
|
||
#viewer.zoomed {
|
||
overflow: visible;
|
||
width: fit-content;
|
||
height: fit-content;
|
||
max-height: none;
|
||
}
|
||
|
||
#zoomControls {
|
||
position: absolute;
|
||
top: 8px;
|
||
right: 8px;
|
||
z-index: 10;
|
||
background: rgba(255,255,255,0.95);
|
||
border-radius: 4px;
|
||
padding: 4px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
display: none;
|
||
}
|
||
|
||
#zoomControls button {
|
||
padding: 4px 8px;
|
||
margin: 2px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 3px;
|
||
background: white;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
#zoomControls button:hover:not(:disabled) {
|
||
background: #f0f0f0;
|
||
border-color: #999;
|
||
transform: none; /* Override global button transform */
|
||
}
|
||
|
||
#zoomControls button:disabled {
|
||
background: #f8f8f8;
|
||
color: #ccc;
|
||
cursor: not-allowed;
|
||
}
|
||
</style>
|
||
<script src="generator.js"></script>
|
||
<script src="engine.js"></script>
|
||
<script src="file-editor.js"></script>
|
||
</head>
|
||
<body class="internal-mode" style="font-family: Inter, Arial, sans-serif; background:#f5f7fa; margin:20px;">
|
||
|
||
<h1 id="projectName" style="color:#495057; margin-bottom:8px;">Timeline Generator</h1>
|
||
<p id="projectSubtitle" style="color:#5C6B7A; margin-top:0; margin-bottom:16px;">
|
||
Lade Projektdateien um eine Timeline zu erstellen oder verwende den lokalen Server für automatisches Laden.<br>
|
||
<small style="color:#6c757d;">💡 Zoom: Verwende die Zoom-Buttons oder Strg+Scrollrad für große Timelines.</small>
|
||
</p>
|
||
|
||
<!-- Integrated File Management -->
|
||
<div id="fileManager" style="margin-bottom:16px; padding:16px; background:#f8f9fa; border:1px solid #e9ecef; border-radius:8px;">
|
||
<h3 style="margin:0 0 12px 0; font-size:14px; font-weight:600; color:#495057;">Project Files</h3>
|
||
|
||
<!-- Project Folder Picker -->
|
||
<div style="margin-bottom:16px; padding:16px; background:#e7f3ff; border:2px solid #0066cc; border-radius:6px;">
|
||
<div style="text-align:center; margin-bottom:8px;">
|
||
<label class="upload-btn" style="background:#0066cc; font-size:14px; padding:10px 20px;">
|
||
<input type="file" id="folderInput" webkitdirectory directory multiple style="display:none;" />
|
||
📂 Load Project Folder
|
||
</label>
|
||
</div>
|
||
<div style="text-align:center;">
|
||
<span style="font-size:13px; color:#004080; font-weight:500;">
|
||
Select your project folder to load all files automatically
|
||
</span><br>
|
||
<span style="font-size:11px; color:#0066cc;">
|
||
(project.json, template-v2.svg, style.css, sample.csv)
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="file-grid" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap:12px; margin-bottom:16px;">
|
||
<div class="file-item">
|
||
<div class="file-header">
|
||
<span class="file-label">Project Configuration</span>
|
||
<button class="edit-btn" id="editProjectBtn" disabled>✏️ Edit</button>
|
||
</div>
|
||
<div class="file-status">
|
||
<span id="projectFile" class="file-name">Not loaded</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="file-item">
|
||
<div class="file-header">
|
||
<span class="file-label">SVG Template</span>
|
||
<button class="edit-btn" id="editSvgBtn" disabled>✏️ Edit</button>
|
||
</div>
|
||
<div class="file-status">
|
||
<span id="svgFile" class="file-name">Not loaded</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="file-item">
|
||
<div class="file-header">
|
||
<span class="file-label">Stylesheet</span>
|
||
<button class="edit-btn" id="editCssBtn" disabled>✏️ Edit</button>
|
||
</div>
|
||
<div class="file-status">
|
||
<span id="cssFile" class="file-name">Not loaded</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="file-item">
|
||
<div class="file-header">
|
||
<span class="file-label">CSV Data</span>
|
||
<button class="edit-btn" id="editCsvBtn" disabled>✏️ Edit</button>
|
||
</div>
|
||
<div class="file-status">
|
||
<span id="csvFile" class="file-name">Not loaded</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="controls" style="display:flex; flex-wrap:wrap; gap:12px; justify-content:center; padding-top:12px; border-top:1px solid #dee2e6;">
|
||
<button id="toggleView" style="padding:8px 16px; background:#495057; color:white; border:none; border-radius:6px; cursor:pointer; font-size:12px;">
|
||
🔄 Switch View (Internal / External)
|
||
</button>
|
||
<button id="saveChanges" disabled
|
||
style="padding:8px 16px; background:#28a745; color:white; border:none; border-radius:6px; cursor:pointer; opacity:0.6; font-size:12px;">
|
||
💾 Save Changes
|
||
</button>
|
||
<button id="downloadSvg" disabled
|
||
style="padding:8px 16px; background:#495057; color:white; border:none; border-radius:6px; cursor:pointer; opacity:0.6; font-size:12px;">
|
||
📥 Download SVG
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SVG Viewer with zoom and scroll capabilities -->
|
||
<div id="viewerContainer">
|
||
<!-- Zoom controls -->
|
||
<div id="zoomControls">
|
||
<button id="zoomIn">🔍+</button>
|
||
<button id="zoomOut">🔍-</button>
|
||
<button id="zoomReset">100%</button>
|
||
</div>
|
||
|
||
<!-- Scrollable viewer -->
|
||
<div id="viewer">
|
||
<div style="text-align:center; padding:40px 20px; color:#6c757d;">
|
||
<div style="font-size:48px; margin-bottom:16px;">📊</div>
|
||
<h4 style="margin:0 0 8px 0; color:#495057;">Keine Timeline verfügbar</h4>
|
||
<p style="margin:0; font-size:14px;">
|
||
Lade eine <strong>Projektkonfiguration</strong> oder <strong>CSV-Datei</strong> um zu beginnen.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Debug Information Panel -->
|
||
<div id="debugInfo" style="margin-top:16px; padding:16px; background:#f8f9fa; border:1px solid #e9ecef; border-radius:8px; display:none;">
|
||
<h3 style="margin:0 0 12px 0; font-size:14px; font-weight:600; color:#495057;">🔍 Debug Information</h3>
|
||
|
||
<!-- Field Mappings -->
|
||
<div id="fieldMappingInfo" style="display:none; margin-bottom:12px;">
|
||
<h4 style="margin:0 0 8px 0; font-size:13px; color:#6c757d; font-weight:600;">📋 Configured Field Mappings</h4>
|
||
<pre id="fieldMappingDisplay" style="background:#fff; padding:12px; border-radius:4px; border:1px solid #dee2e6; font-size:12px; margin:0; overflow-x:auto; font-family:'Courier New', monospace;"></pre>
|
||
</div>
|
||
|
||
<!-- Template Fields -->
|
||
<div id="templateFieldsInfo" style="display:none; margin-bottom:12px;">
|
||
<h4 style="margin:0 0 8px 0; font-size:13px; color:#6c757d; font-weight:600;">🖼️ Template Placeholders</h4>
|
||
<pre id="templateFieldsDisplay" style="background:#fff; padding:12px; border-radius:4px; border:1px solid #dee2e6; font-size:12px; margin:0; overflow-x:auto; font-family:'Courier New', monospace;"></pre>
|
||
</div>
|
||
|
||
<!-- CSV Data Preview -->
|
||
<div id="csvDataInfo" style="display:none;">
|
||
<h4 style="margin:0 0 8px 0; font-size:13px; color:#6c757d; font-weight:600;">📊 CSV Data Preview</h4>
|
||
<pre id="csvDataDisplay" style="background:#fff; padding:12px; border-radius:4px; border:1px solid #dee2e6; font-size:12px; margin:0; overflow-x:auto; font-family:'Courier New', monospace;"></pre>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
// Setup event handlers first
|
||
if (typeof setupEventHandlers === 'function') {
|
||
setupEventHandlers();
|
||
console.log("Event handlers set up");
|
||
}
|
||
|
||
// Initialize file editor
|
||
if (window.fileEditor && typeof window.fileEditor.init === 'function') {
|
||
window.fileEditor.init();
|
||
console.log("File editor initialized");
|
||
}
|
||
|
||
// Initialize zoom functionality
|
||
if (window.svgViewer && typeof window.svgViewer.initializeZoom === 'function') {
|
||
window.svgViewer.initializeZoom();
|
||
console.log("SVG zoom initialized");
|
||
}
|
||
|
||
// Ensure engines are loaded before auto-loading
|
||
function tryAutoLoad() {
|
||
if (window.timelineEngine && window.timelineGenerator) {
|
||
console.log("Both engines loaded, starting auto-load");
|
||
// Only try auto-load if not running from file:// protocol
|
||
if (location.protocol !== 'file:') {
|
||
window.timelineEngine.autoLoadDefaultProject();
|
||
} else {
|
||
console.log("Running from file:// protocol - auto-load disabled due to CORS");
|
||
document.getElementById("viewer").innerHTML =
|
||
"<div style='text-align:center; padding:40px 20px; color:#6c757d;'>" +
|
||
"<div style='font-size:48px; margin-bottom:16px;'>📁</div>" +
|
||
"<h4 style='margin:0 0 8px 0; color:#495057;'>Manuelle Dateien laden</h4>" +
|
||
"<p style='margin:0; font-size:14px;'>" +
|
||
"Verwende die <strong>Load</strong>-Buttons oben um Projektdateien zu laden.<br>" +
|
||
"<small>💡 Tipp: Für automatisches Laden verwende einen lokalen Server (z.B. <code>make serve</code>)</small>" +
|
||
"</p></div>";
|
||
}
|
||
} else {
|
||
console.log("Engines not ready, retrying...", {
|
||
engine: !!window.timelineEngine,
|
||
generator: !!window.timelineGenerator
|
||
});
|
||
setTimeout(tryAutoLoad, 50);
|
||
}
|
||
}
|
||
tryAutoLoad();
|
||
});
|
||
</script>
|
||
|
||
<!-- Editor Modal -->
|
||
<div id="editorModal" class="editor-modal">
|
||
<div class="editor-content">
|
||
<div class="editor-header">
|
||
<h3 id="editorTitle" style="margin:0; font-size:16px; font-weight:600;">Edit File</h3>
|
||
<button onclick="window.fileEditor.closeEditor()" style="background:none; border:none; font-size:24px; cursor:pointer; color:#6c757d;">×</button>
|
||
</div>
|
||
<div class="editor-body">
|
||
<textarea id="editorTextarea" class="editor-textarea" spellcheck="false"></textarea>
|
||
</div>
|
||
<div class="editor-footer">
|
||
<button onclick="window.fileEditor.closeEditor()" style="padding:8px 16px; background:#6c757d; color:white; border:none; border-radius:4px; cursor:pointer;">
|
||
Cancel
|
||
</button>
|
||
<button onclick="window.fileEditor.applyChanges()" style="padding:8px 16px; background:#007bff; color:white; border:none; border-radius:4px; cursor:pointer;">
|
||
Apply Changes
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|