- Fix unused variable errors by removing unused parameters or using proper destructuring - Fix 'prefer-const' violations by replacing 'let' with 'const' where appropriate - Fix lexical declaration errors in switch cases by adding proper block scoping - Replace explicit 'any' type with proper TypeScript interface for DOMPurify config - Fix React hooks dependency warnings in useDialog hook - Remove unused imports and variables throughout codebase Specific fixes: - Replace '_' parameters with proper destructuring syntax ([, value]) - Add block scopes to switch case statements in templateRenderer.ts - Improve type safety in htmlSanitizer.ts with explicit DOMPurify interface - Fix useCallback dependencies in useDialog hook - Remove unused 'placeholder' parameter in generateSampleDataForSlot All 15 ESLint errors have been resolved, improving code maintainability and consistency. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
234 lines
7.9 KiB
TypeScript
234 lines
7.9 KiB
TypeScript
import type { SlideLayout, SlotConfig } from '../types/theme';
|
|
|
|
/**
|
|
* Creates a simple SVG pattern for image slots
|
|
*/
|
|
const createImageSVG = (slotName: string, width: number = 400, height: number = 300): string => {
|
|
const encodedSVG = encodeURIComponent(`
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
<defs>
|
|
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
|
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="#e2e8f0" stroke-width="1"/>
|
|
</pattern>
|
|
</defs>
|
|
<rect width="100%" height="100%" fill="#f8fafc"/>
|
|
<rect width="100%" height="100%" fill="url(#grid)"/>
|
|
<rect x="2" y="2" width="${width-4}" height="${height-4}" fill="none" stroke="#cbd5e1" stroke-width="2" stroke-dasharray="8,4"/>
|
|
<text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle"
|
|
font-family="system-ui, sans-serif" font-size="16" font-weight="500" fill="#64748b">
|
|
${slotName}
|
|
</text>
|
|
</svg>
|
|
`);
|
|
return `data:image/svg+xml,${encodedSVG}`;
|
|
};
|
|
|
|
/**
|
|
* Sample content templates for more realistic previews
|
|
*/
|
|
const SAMPLE_CONTENT = {
|
|
title: [
|
|
'Quarterly Sales Review',
|
|
'Project Roadmap 2024',
|
|
'Market Analysis Report',
|
|
'New Product Launch',
|
|
'Team Performance Update'
|
|
],
|
|
subtitle: [
|
|
'Q4 Results and Future Outlook',
|
|
'Strategic Planning and Key Milestones',
|
|
'Customer Insights and Trends',
|
|
'Innovation and Growth Strategy',
|
|
'Achievements and Next Steps'
|
|
],
|
|
heading: [
|
|
'Key Findings',
|
|
'Next Steps',
|
|
'Executive Summary',
|
|
'Strategic Goals',
|
|
'Market Opportunities'
|
|
],
|
|
text: [
|
|
'This comprehensive analysis provides insights into market trends and customer behavior patterns that will shape our strategic decisions moving forward.',
|
|
'Our team has achieved significant milestones this quarter, demonstrating strong execution and commitment to delivering exceptional results.',
|
|
'The data shows promising growth opportunities in emerging markets, with particular strength in the technology and healthcare sectors.',
|
|
'Customer feedback indicates high satisfaction levels with our current offerings, while highlighting areas for continued innovation.',
|
|
'Looking ahead, we will focus on expanding our market presence while maintaining our commitment to quality and customer excellence.'
|
|
],
|
|
list: [
|
|
'• Increase market share by 15%\n• Launch 3 new products\n• Expand to 5 new regions',
|
|
'• Improve customer satisfaction\n• Reduce operational costs\n• Enhance digital capabilities',
|
|
'• Strengthen brand recognition\n• Optimize supply chain\n• Invest in talent development'
|
|
]
|
|
};
|
|
|
|
/**
|
|
* Generates sample data for a slot based on its configuration
|
|
*/
|
|
export const generateSampleDataForSlot = (slot: SlotConfig): string => {
|
|
const { id, type, defaultContent } = slot;
|
|
|
|
// Use default content if available
|
|
if (defaultContent && defaultContent.trim()) {
|
|
return defaultContent;
|
|
}
|
|
|
|
// Clean up slot name for display
|
|
const slotDisplayName = id.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
|
|
// Generate sample data based on slot type
|
|
switch (type) {
|
|
case 'image':
|
|
return createImageSVG(slotDisplayName);
|
|
|
|
case 'title': {
|
|
const titleSamples = SAMPLE_CONTENT.title;
|
|
return titleSamples[Math.floor(Math.random() * titleSamples.length)];
|
|
}
|
|
|
|
case 'subtitle': {
|
|
const subtitleSamples = SAMPLE_CONTENT.subtitle;
|
|
return subtitleSamples[Math.floor(Math.random() * subtitleSamples.length)];
|
|
}
|
|
|
|
case 'heading': {
|
|
const headingSamples = SAMPLE_CONTENT.heading;
|
|
return headingSamples[Math.floor(Math.random() * headingSamples.length)];
|
|
}
|
|
|
|
case 'text': {
|
|
const textSamples = SAMPLE_CONTENT.text;
|
|
return textSamples[Math.floor(Math.random() * textSamples.length)];
|
|
}
|
|
|
|
case 'video':
|
|
return createImageSVG(`${slotDisplayName} (Video)`, 640, 360);
|
|
|
|
case 'audio':
|
|
return `[${slotDisplayName} Audio]`;
|
|
|
|
case 'list': {
|
|
const listSamples = SAMPLE_CONTENT.list;
|
|
return listSamples[Math.floor(Math.random() * listSamples.length)];
|
|
}
|
|
|
|
case 'code':
|
|
return `// ${slotDisplayName}\nfunction ${id.replace(/-/g, '')}() {\n return "${slotDisplayName}";\n}`;
|
|
|
|
case 'table':
|
|
return `${slotDisplayName} Col 1 | ${slotDisplayName} Col 2\n--- | ---\nRow 1 Data | Row 1 Data\nRow 2 Data | Row 2 Data`;
|
|
|
|
default:
|
|
return slotDisplayName;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generates a complete sample data object for all slots in a layout
|
|
*/
|
|
export const generateSampleDataForLayout = (layout: SlideLayout): Record<string, string> => {
|
|
const sampleData: Record<string, string> = {};
|
|
|
|
layout.slots.forEach(slot => {
|
|
sampleData[slot.id] = generateSampleDataForSlot(slot);
|
|
|
|
// Add related data for image slots
|
|
if (slot.type === 'image') {
|
|
sampleData[`${slot.id}Alt`] = `Sample ${slot.id} description`;
|
|
}
|
|
});
|
|
|
|
// Add common template variables that might be used
|
|
sampleData.footerText = 'Sample Footer Text';
|
|
sampleData.author = 'Sample Author';
|
|
sampleData.date = new Date().toLocaleDateString();
|
|
|
|
// Add layout metadata for templates that might use it
|
|
sampleData['layout-description'] = layout.description || `This is the ${layout.name} layout`;
|
|
sampleData['layout-name'] = layout.name;
|
|
|
|
return sampleData;
|
|
};
|
|
|
|
/**
|
|
* Renders an HTML template with sample data using simple string replacement
|
|
*/
|
|
export const renderTemplateWithSampleData = (
|
|
htmlTemplate: string,
|
|
layout: SlideLayout
|
|
): string => {
|
|
const sampleData = generateSampleDataForLayout(layout);
|
|
let rendered = htmlTemplate;
|
|
|
|
// Handle Handlebars-style templates: {{variable}}
|
|
Object.entries(sampleData).forEach(([key, value]) => {
|
|
const simpleRegex = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
|
|
rendered = rendered.replace(simpleRegex, value);
|
|
});
|
|
|
|
// Handle conditional blocks: {{#variable}}...{{/variable}}
|
|
Object.entries(sampleData).forEach(([key, value]) => {
|
|
const conditionalRegex = new RegExp(`\\{\\{#${key}\\}\\}([\\s\\S]*?)\\{\\{/${key}\\}\\}`, 'g');
|
|
if (value && value.trim()) {
|
|
// Replace conditional block with content, substituting variables within
|
|
rendered = rendered.replace(conditionalRegex, (_match, content) => {
|
|
// Replace variables within the conditional block
|
|
let blockContent = content;
|
|
Object.entries(sampleData).forEach(([innerKey, innerValue]) => {
|
|
const innerRegex = new RegExp(`\\{\\{${innerKey}\\}\\}`, 'g');
|
|
blockContent = blockContent.replace(innerRegex, innerValue);
|
|
});
|
|
return blockContent;
|
|
});
|
|
} else {
|
|
// Remove conditional block if no data
|
|
rendered = rendered.replace(conditionalRegex, '');
|
|
}
|
|
});
|
|
|
|
// Clean up any remaining template variables
|
|
rendered = rendered.replace(/\{\{[^}]+\}\}/g, '');
|
|
|
|
return rendered;
|
|
};
|
|
|
|
/**
|
|
* Creates a preview-ready HTML document with theme CSS applied
|
|
*/
|
|
export const createPreviewDocument = (
|
|
renderedTemplate: string,
|
|
themeCssUrl: string
|
|
): string => {
|
|
return `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="stylesheet" href="${themeCssUrl}">
|
|
<style>
|
|
html, body {
|
|
margin: 0;
|
|
padding: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
overflow: hidden;
|
|
}
|
|
.preview-container {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
transform: scale(0.25);
|
|
transform-origin: top left;
|
|
overflow: hidden;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="preview-container">
|
|
${renderedTemplate}
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}; |