(images: string[])
| 18 | * Generate the comparison board HTML page. |
| 19 | */ |
| 20 | export function generateCompareHtml(images: string[]): string { |
| 21 | const variantLabels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| 22 | |
| 23 | const variantCards = images.map((imgPath, i) => { |
| 24 | const label = variantLabels[i] || `${i + 1}`; |
| 25 | // Embed images as base64 data URIs for self-contained HTML |
| 26 | const imgData = fs.readFileSync(imgPath).toString("base64"); |
| 27 | const ext = path.extname(imgPath).slice(1) || "png"; |
| 28 | |
| 29 | return ` |
| 30 | <div class="variant" data-variant="${label}"> |
| 31 | <div class="variant-header"> |
| 32 | <span class="variant-label">Option ${label}</span> |
| 33 | <span class="variant-desc" id="variant-desc-${label}">Design direction ${label}</span> |
| 34 | </div> |
| 35 | <img src="data:image/${ext};base64,${imgData}" alt="Option ${label}" /> |
| 36 | <div class="variant-controls"> |
| 37 | <label class="pick-label"> |
| 38 | <input type="radio" name="preferred" value="${label}" /> |
| 39 | <span class="pick-text">Pick</span> |
| 40 | <span class="pick-confirm" style="display:none;">We'll move forward with Option ${label}</span> |
| 41 | </label> |
| 42 | <div class="stars" data-variant="${label}"> |
| 43 | ${[1,2,3,4,5].map(n => `<span class="star" data-value="${n}">★</span>`).join("")} |
| 44 | </div> |
| 45 | <input type="text" class="feedback-input" data-variant="${label}" |
| 46 | placeholder="What do you like/dislike?" /> |
| 47 | <button class="more-like-this" data-variant="${label}">More like this</button> |
| 48 | </div> |
| 49 | </div>`; |
| 50 | }).join("\n"); |
| 51 | |
| 52 | return `<!DOCTYPE html> |
| 53 | <html lang="en"> |
| 54 | <head> |
| 55 | <meta charset="utf-8" /> |
| 56 | <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 57 | <title>Design Exploration</title> |
| 58 | <style> |
| 59 | * { margin: 0; padding: 0; box-sizing: border-box; } |
| 60 | body { |
| 61 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; |
| 62 | background: #fff; |
| 63 | color: #333; |
| 64 | } |
| 65 | |
| 66 | .header { |
| 67 | padding: 16px 24px; |
| 68 | border-bottom: 1px solid #e5e5e5; |
| 69 | display: flex; |
| 70 | justify-content: space-between; |
| 71 | align-items: center; |
| 72 | } |
| 73 | .header h1 { font-size: 16px; font-weight: 600; } |
| 74 | .header .meta { font-size: 13px; color: #999; display: flex; align-items: center; gap: 12px; } |
| 75 | |
| 76 | .view-toggle { |
| 77 | display: flex; |
no outgoing calls
no test coverage detected