UNPKG

html-ad-generator-mcp

Version:

MCP server for generating HTML ad templates from JSON input for Google Ads, Meta Ads, and Moment Science

126 lines 8.01 kB
import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); export function processGoogleTemplate(input) { // Read the base template const templatePath = join(__dirname, '../../templates/google-base.html'); let html = readFileSync(templatePath, 'utf-8'); // ---- Populate search ad dropdowns dynamically ---- if (input.searchAd) { function insertOptions(selectRegex, options, fieldId) { const match = html.match(selectRegex); if (match) { const existing = match[1]; let newOptions = ''; options.forEach(opt => { if (opt && !existing.includes(opt)) { newOptions += `\n <div class="dropdown-option" onclick="selectOption('${fieldId}', this)">${escapeHtml(opt)}</div>`; } }); html = html.replace(selectRegex, `<div class="dropdown-options" id="${fieldId.split('-')[0]}-options">${newOptions}${existing}</div>`); } } // Headlines insertOptions(/<div class="dropdown-options" id="search-headline-options">([\s\S]*?)<\/div>/, input.searchAd.headlines, 'search-headline-text'); // Descriptions insertOptions(/<div class="dropdown-options" id="search-description-options">([\s\S]*?)<\/div>/, input.searchAd.descriptions, 'search-description-text'); // Update preview const headlinePreview = input.searchAd.headlines.join(' - '); html = html.replace(/<div class="search-ad-headline" id="search-preview-headline">.*?<\/div>/, `<div class="search-ad-headline" id="search-preview-headline">${escapeHtml(headlinePreview)}</div>`); if (input.searchAd.descriptions[0]) { html = html.replace(/<div class="search-ad-description" id="search-preview-description-1">.*?<\/div>/, `<div class="search-ad-description" id="search-preview-description-1">${escapeHtml(input.searchAd.descriptions[0])}</div>`); } if (input.searchAd.descriptions[1]) { html = html.replace(/<div class="search-ad-description" id="search-preview-description-2">.*?<\/div>/, `<div class="search-ad-description" id="search-preview-description-2">${escapeHtml(input.searchAd.descriptions[1])}</div>`); } } // ---- Populate display ad dropdowns dynamically ---- if (input.displayAd) { function insertDisplayOptions(selectRegex, options, fieldId) { const match = html.match(selectRegex); if (match) { const existing = match[1]; let newOptions = ''; options.forEach(opt => { if (opt && !existing.includes(opt)) { newOptions += `\n <div class="dropdown-option" onclick="selectOption('${fieldId}', this)">${escapeHtml(opt)}</div>`; } }); html = html.replace(selectRegex, `<div class="dropdown-options" id="${fieldId.split('-')[0]}-options">${newOptions}${existing}</div>`); } } function insertDisplayOption(selectRegex, option, fieldId) { const match = html.match(selectRegex); if (match && !match[1].includes(option)) { const newOption = `\n <div class="dropdown-option" onclick="selectOption('${fieldId}', this)">${escapeHtml(option)}</div>`; html = html.replace(selectRegex, `<div class="dropdown-options" id="${fieldId.split('-')[0]}-options">${newOption}${match[1]}</div>`); } } // Use array handling for content fields insertDisplayOptions(/<div class="dropdown-options" id="display-headline-options">([\s\S]*?)<\/div>/, input.displayAd.headline, 'display-headline-text'); insertDisplayOptions(/<div class="dropdown-options" id="display-long-headline-options">([\s\S]*?)<\/div>/, input.displayAd.longHeadline, 'display-long-headline-text'); insertDisplayOptions(/<div class="dropdown-options" id="display-description-options">([\s\S]*?)<\/div>/, input.displayAd.description, 'display-description-text'); // Business name is still single value insertDisplayOption(/<div class="dropdown-options" id="display-business-options">([\s\S]*?)<\/div>/, input.displayAd.businessName, 'display-business-text'); // Update preview fields - use first element from arrays html = html.replace(/<div class="display-ad-headline" id="display-preview-headline">.*?<\/div>/, `<div class="display-ad-headline" id="display-preview-headline">${escapeHtml(input.displayAd.headline[0] || '')}</div>`); html = html.replace(/<div class="display-ad-headline" id="display-preview-long-headline">.*?<\/div>/, `<div class="display-ad-headline" id="display-preview-long-headline">${escapeHtml(input.displayAd.longHeadline[0] || '')}</div>`); html = html.replace(/<div class="display-ad-description" id="display-preview-description">.*?<\/div>/, `<div class="display-ad-description" id="display-preview-description">${escapeHtml(input.displayAd.description[0] || '')}</div>`); html = html.replace(/<div class="display-ad-business" id="display-preview-business">.*?<\/div>/, `<div class="display-ad-business" id="display-preview-business">${escapeHtml(input.displayAd.businessName)}</div>`); // Update image if provided if (input.displayAd.imageUrl) { html = html.replace(/src="https:\/\/picsum\.photos\/seed\/fashion123\/600\/600\.jpg"/, `src="${escapeHtml(input.displayAd.imageUrl)}"`); } } // Update character counts in JavaScript html = updateCharacterCounts(html, input); return html; } function getDefaultHeadline(index) { const defaults = ['Premium Running Shoes', 'Lightweight & Comfortable', 'Shop Now - 50% Off']; return defaults[index] || ''; } function getDefaultDescription(index) { const defaults = [ 'Experience ultimate comfort with our premium running shoes. Perfect for all terrains.', 'Limited time offer: Buy 1 get 1 free. Free shipping on orders over $50.' ]; return defaults[index] || ''; } function escapeHtml(text) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }; return text.replace(/[&<>"']/g, m => map[m]); } function escapeRegex(text) { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function updateCharacterCounts(html, input) { if (input.searchAd) { input.searchAd.headlines.forEach((headline, index) => { const countId = `search-headline-${index + 1}-count`; const regex = new RegExp(`id="${countId}">\\d+/30</div>`); html = html.replace(regex, `id="${countId}">${headline.length}/30</div>`); }); input.searchAd.descriptions.forEach((desc, index) => { const countId = `search-description-${index + 1}-count`; const regex = new RegExp(`id="${countId}">\\d+/90</div>`); html = html.replace(regex, `id="${countId}">${desc.length}/90</div>`); }); } if (input.displayAd) { // Use first element from arrays for character counts html = html.replace(/id="display-headline-count">\d+\/30</, `id="display-headline-count">${(input.displayAd.headline[0] || '').length}/30<`); html = html.replace(/id="display-long-headline-count">\d+\/90</, `id="display-long-headline-count">${(input.displayAd.longHeadline[0] || '').length}/90<`); html = html.replace(/id="display-description-count">\d+\/90</, `id="display-description-count">${(input.displayAd.description[0] || '').length}/90<`); html = html.replace(/id="display-business-count">\d+\/25</, `id="display-business-count">${input.displayAd.businessName.length}/25<`); } return html; } //# sourceMappingURL=google.js.map