feat: comprehensive SVG viewer enhancements with zoom and template preview

- Add interactive zoom functionality (25%-300% with Ctrl+scroll wheel)
- Fix SVG width constraints by injecting calculated dimensions into templates
- Implement template preview with theme-aware sample data
- Enhance UI layout with file reordering (Project → Template → CSS → Data)
- Add blue theme support for my-project alongside existing green theme
- Fix my-project field mapping from empty 'Übergeordnet' to 'Status'
- Improve SVG viewport handling with proper scrolling and container management
- Add visual zoom controls with percentage display and smart visibility

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 00:46:58 +01:00
parent 02bcf69096
commit bc756fa0cd
6 changed files with 524 additions and 33 deletions

View File

@@ -48,13 +48,18 @@ window.timelineGenerator = {
// Enhanced styling when using external template
if (template && template.includes('Enhanced')) {
// Determine color scheme based on template content
const isBlueTheme = template.includes('My Project');
const primaryColor = isBlueTheme ? '#3b82f6' : '#2d8659';
const secondaryColor = isBlueTheme ? '#60a5fa' : '#4a9b6b';
// More prominent month indicators for external template
monthGraphics += `<line x1="${x}" y1="${gridTop}" x2="${x}" y2="${gridBottom}" stroke="#4a9b6b" stroke-width="2" opacity="0.6" />`;
monthGraphics += `<rect x="${x-30}" y="${monthLabelY-20}" width="60" height="25" fill="#2d8659" opacity="0.1" rx="4" />`;
monthGraphics += `<text x="${x + 4}" y="${monthLabelY}" fill="#2d8659" font-size="13" font-weight="600">${label}</text>`;
monthGraphics += `<line x1="${x}" y1="${gridTop}" x2="${x}" y2="${gridBottom}" stroke="${secondaryColor}" stroke-width="2" opacity="0.6" />`;
monthGraphics += `<rect x="${x-30}" y="${monthLabelY-20}" width="60" height="25" fill="${primaryColor}" opacity="0.1" rx="4" />`;
monthGraphics += `<text x="${x + 4}" y="${monthLabelY}" fill="${primaryColor}" font-size="13" font-weight="600">${label}</text>`;
// Add month separator
if (i > 0) {
monthGraphics += `<rect x="${x-1}" y="${gridTop}" width="2" height="${gridBottom-gridTop}" fill="#4a9b6b" opacity="0.3" />`;
monthGraphics += `<rect x="${x-1}" y="${gridTop}" width="2" height="${gridBottom-gridTop}" fill="${secondaryColor}" opacity="0.3" />`;
}
} else {
// Default styling
@@ -76,10 +81,15 @@ window.timelineGenerator = {
// Enhanced styling when using external template
if (template && template.includes('Enhanced')) {
// Determine color scheme based on template content
const isBlueTheme = template.includes('My Project');
const primaryColor = isBlueTheme ? '#3b82f6' : '#2d8659';
const secondaryColor = isBlueTheme ? '#60a5fa' : '#4a9b6b';
// More subtle lane borders for enhanced template
laneBlocks += `<rect x="40" y="${laneY - 24}" width="${left + months.length * monthWidth}" height="${laneHeight}" fill="rgba(255,255,255,0.7)" stroke="#4a9b6b" stroke-width="1" opacity="0.5" rx="8" />`;
laneBlocks += `<rect x="40" y="${laneY - 24}" width="${left + months.length * monthWidth}" height="${laneHeight}" fill="rgba(255,255,255,0.7)" stroke="${secondaryColor}" stroke-width="1" opacity="0.5" rx="8" />`;
// Enhanced lane label
laneBlocks += `<text x="56" y="${laneY - 4}" fill="#2d8659" font-size="14" font-weight="700">${this.escapeXml(laneName)}</text>`;
laneBlocks += `<text x="56" y="${laneY - 4}" fill="${primaryColor}" font-size="14" font-weight="700">${this.escapeXml(laneName)}</text>`;
} else {
// Default lane styling
laneBlocks += `<rect x="40" y="${laneY - 24}" width="${left + months.length * monthWidth}" height="${laneHeight}" fill="#FFFFFF" stroke="#E3E8EF" rx="10" />`;
@@ -96,10 +106,16 @@ window.timelineGenerator = {
// Enhanced task item styling for external template
if (template && template.includes('Enhanced')) {
laneBlocks += `<circle cx="${cx}" cy="${cy}" r="6" fill="#2d8659" stroke="#4a9b6b" stroke-width="2" />`;
laneBlocks += `<text x="${cx + 12}" y="${cy + 4}" font-size="12" fill="#2d8659" font-weight="500">
// Determine color scheme based on template content
const isBlueTheme = template.includes('My Project');
const primaryColor = isBlueTheme ? '#3b82f6' : '#2d8659';
const secondaryColor = isBlueTheme ? '#60a5fa' : '#4a9b6b';
const darkColor = isBlueTheme ? '#1e40af' : '#1e5a3d';
laneBlocks += `<circle cx="${cx}" cy="${cy}" r="6" fill="${primaryColor}" stroke="${secondaryColor}" stroke-width="2" />`;
laneBlocks += `<text x="${cx + 12}" y="${cy + 4}" font-size="12" fill="${primaryColor}" font-weight="500">
<tspan class="item-id">${this.escapeXml(it.id || "")}: </tspan>
<tspan class="item-title" fill="#1e5a3d">${this.escapeXml(it.title || "")}</tspan>
<tspan class="item-title" fill="${darkColor}">${this.escapeXml(it.title || "")}</tspan>
</text>`;
} else {
// Default task item styling
@@ -113,9 +129,22 @@ window.timelineGenerator = {
});
if (template && template.includes("{{MONTHS}}") && template.includes("{{LANES}}")) {
return template
// Calculate dimensions for template
const height = top + laneNames.length * (laneHeight + laneGap) + 80;
const width = left + months.length * monthWidth + 100;
// Replace placeholders and inject calculated dimensions
let processedTemplate = template
.replace("{{MONTHS}}", monthGraphics)
.replace("{{LANES}}", laneBlocks);
// Add width and height attributes to the SVG element
processedTemplate = processedTemplate.replace(
/<svg([^>]*?)>/,
`<svg$1 width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`
);
return processedTemplate;
}
// Fallback: embed directly in simple SVG