Loading...
Loading...
Compare original and translation side by side
/home/user/gestante.png/mnt/user-data/uploads/foto.jpg/home/user/gestante.png/mnt/user-data/uploads/foto.jpggestante.pngbackground: url(filepath)data:Path.write_text()$gestante.pngbackground: url(filepath)data:Path.write_text()$undefinedundefined
```python
import base64
from pathlib import Path
```python
import base64
from pathlib import Pathfilefileundefinedundefined<!-- Inside the slide div, before any content -->
<img src="{data_uri}"
style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;">
<!-- Semi-transparent overlay so text stays readable -->
<div style="position:absolute;inset:0;background:rgba(255,255,255,0.35);z-index:1;"></div>
<!-- All slide content must have z-index:2 or higher -->rgba(0,0,0,0.45)<!-- 在幻灯片div内,所有内容之前 -->
<img src="{data_uri}"
style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;">
<!-- 半透明覆盖层,确保文字可读 -->
<div style="position:absolute;inset:0;background:rgba(255,255,255,0.35);z-index:1;"></div>
<!-- 所有幻灯片内容的z-index必须为2或更高 -->rgba(0,0,0,0.45)| Mistake | What goes wrong | Fix |
|---|---|---|
| Broken image — relative path only works if HTML and image share the same folder | Always use base64 |
| Browser parser crash, 1.3M token context | Use |
Generating HTML via shell | | Always use Python |
Assuming | File may actually be JPEG; wrong MIME type breaks rendering | Run |
| 错误 | 问题后果 | 修复方案 |
|---|---|---|
| 图片加载失败 —— 相对路径仅在HTML与图片同文件夹时生效 | 始终使用base64 |
| 浏览器解析崩溃,占用1.3M以上上下文令牌 | 使用 |
通过Shell | base64中的 | 始终使用Python |
假设 | 文件实际可能为JPEG;错误的MIME类型会导致渲染失败 | 运行 |
BRAND_PRIMARY = {user's color} // Main accent — progress bar, icons, tags
BRAND_LIGHT = {primary lightened ~20%} // Secondary accent — tags on dark, pills
BRAND_DARK = {primary darkened ~30%} // CTA text, gradient anchor
LIGHT_BG = {warm or cool off-white} // Light slide background (never pure #fff)
LIGHT_BORDER = {slightly darker than LIGHT_BG} // Dividers on light slides
DARK_BG = {near-black with brand tint} // Dark slide backgroundlinear-gradient(165deg, BRAND_DARK 0%, BRAND_PRIMARY 50%, BRAND_LIGHT 100%)BRAND_PRIMARY = {用户提供的颜色} // 主强调色 —— 进度条、图标、标签
BRAND_LIGHT = {主色调提亮约20%} // 次强调色 —— 深色背景上的标签、胶囊按钮
BRAND_DARK = {主色调加深约30%} // CTA文字、渐变锚点色
LIGHT_BG = {暖调或冷调米白色} // 浅色幻灯片背景(永远不要用纯#fff)
LIGHT_BORDER = {比LIGHT_BG稍深的颜色} // 浅色幻灯片上的分隔线
DARK_BG = {带品牌色调的近黑色} // 深色幻灯片背景linear-gradient(165deg, BRAND_DARK 0%, BRAND_PRIMARY 50%, BRAND_LIGHT 100%)| Style | Heading Font | Body Font |
|---|---|---|
| Editorial / premium | Playfair Display | DM Sans |
| Modern / clean | Plus Jakarta Sans (700) | Plus Jakarta Sans (400) |
| Warm / approachable | Lora | Nunito Sans |
| Technical / sharp | Space Grotesk | Space Grotesk |
| Bold / expressive | Fraunces | Outfit |
| Classic / trustworthy | Libre Baskerville | Work Sans |
| Rounded / friendly | Bricolage Grotesque | Bricolage Grotesque |
.serif.sans| 风格 | 标题字体 | 正文字体 |
|---|---|---|
| 编辑感 / 高端 | Playfair Display | DM Sans |
| 现代 / 简洁 | Plus Jakarta Sans (700) | Plus Jakarta Sans (400) |
| 温暖 / 亲和 | Lora | Nunito Sans |
| 科技感 / 锐利 | Space Grotesk | Space Grotesk |
| 大胆 / 有表现力 | Fraunces | Outfit |
| 经典 / 可靠 | Libre Baskerville | Work Sans |
| 圆润 / 友好 | Bricolage Grotesque | Bricolage Grotesque |
.serif.sans| Hook format | Example |
|---|---|
| Afirmação polêmica | "Você está usando IA errado" |
| Número + benefício | "7 ferramentas que substituem seu designer" |
| Pergunta que dói | "Por que seus carrosséis têm 0 salvamentos?" |
| Resultado concreto | "Esse post gerou 4.200 seguidores em 3 dias" |
| Inversão de expectativa | "Mais esforço no design = menos alcance" |
| 钩子格式 | 示例 |
|---|---|
| 争议性声明 | "你正在错误地使用AI" |
| 数字+收益 | "7个可以替代设计师的工具" |
| 直击痛点的问题 | "为什么你的轮播图没有被保存?" |
| 具体成果 | "这个帖子3天内吸引了4200名粉丝" |
| 反转预期 | "设计投入越多,曝光量越少" |
| # | Type | Background | Purpose |
|---|---|---|---|
| 1 | Hero | LIGHT_BG | Hook — bold statement, logo lockup, optional watermark |
| 2 | Problem | DARK_BG | Pain point — what's broken, frustrating, or outdated |
| 3 | Solution | Brand gradient | The answer — what solves it, optional quote/prompt box |
| 4 | Features | LIGHT_BG | What you get — feature list with icons |
| 5 | Details | DARK_BG | Depth — customization, specs, differentiators |
| 6 | How-to | LIGHT_BG | Steps — numbered workflow or process |
| 7 | CTA | Brand gradient | Call to action — logo, tagline, CTA button. No arrow. Full progress bar. |
| 序号 | 类型 | 背景 | 目的 |
|---|---|---|---|
| 1 | Hero页 | LIGHT_BG | 钩子 —— 大胆声明、Logo组合、可选水印 |
| 2 | 痛点页 | DARK_BG | 痛点 —— 现存问题、痛点、过时之处 |
| 3 | 解决方案页 | 品牌渐变 | 答案 —— 解决方案,可选引用/提示框 |
| 4 | 功能页 | LIGHT_BG | 核心价值 —— 带图标的功能列表 |
| 5 | 详情页 | DARK_BG | 深度信息 —— 自定义选项、规格、差异化优势 |
| 6 | 操作指南页 | LIGHT_BG | 步骤 —— 编号工作流或流程 |
| 7 | CTA页 | 品牌渐变 | 行动号召 —— Logo、标语、CTA按钮。无箭头,进度条满格。 |
| # | Type | Background |
|---|---|---|
| 1 | Hero | LIGHT_BG |
| 2–N | Item N | Alternating LIGHT/DARK |
| Last | CTA | Brand gradient |
| 序号 | 类型 | 背景 |
|---|---|---|
| 1 | Hero页 | LIGHT_BG |
| 2–N | 第N项 | 交替LIGHT/DARK背景 |
| 最后一张 | CTA页 | 品牌渐变 |
| # | Type | Background |
|---|---|---|
| 1 | Hero | LIGHT_BG |
| 2 | Contexto / Por quê | DARK_BG |
| 3–5 | Passo 1, 2, 3 | Alternating |
| 6 | Resultado esperado | DARK_BG |
| 7 | CTA | Brand gradient |
| 序号 | 类型 | 背景 |
|---|---|---|
| 1 | Hero页 | LIGHT_BG |
| 2 | 背景/原因 | DARK_BG |
| 3–5 | 步骤1、2、3 | 交替背景 |
| 6 | 预期成果 | DARK_BG |
| 7 | CTA页 | 品牌渐变 |
| # | Type | Background |
|---|---|---|
| 1 | Hero (o que será comparado) | LIGHT_BG |
| 2 | Opção A | LIGHT_BG |
| 3 | Opção B | DARK_BG |
| 4 | Veredicto | Brand gradient |
| 5 | CTA | DARK_BG |
| 序号 | 类型 | 背景 |
|---|---|---|
| 1 | Hero页(说明对比主题) | LIGHT_BG |
| 2 | 选项A | LIGHT_BG |
| 3 | 选项B | DARK_BG |
| 4 | 结论 | 品牌渐变 |
| 5 | CTA页 | DARK_BG |
((slideIndex + 1) / totalSlides) * 100%rgba(0,0,0,0.08)BRAND_PRIMARYrgba(0,0,0,0.3)rgba(255,255,255,0.12)#fffrgba(255,255,255,0.4)function progressBar(index, total, isLightSlide) {
const pct = ((index + 1) / total) * 100;
const trackColor = isLightSlide ? 'rgba(0,0,0,0.08)' : 'rgba(255,255,255,0.12)';
const fillColor = isLightSlide ? BRAND_PRIMARY : '#fff'; // use actual BRAND_PRIMARY value
const labelColor = isLightSlide ? 'rgba(0,0,0,0.3)' : 'rgba(255,255,255,0.4)';
return `<div style="position:absolute;bottom:0;left:0;right:0;padding:16px 28px 20px;z-index:10;display:flex;align-items:center;gap:10px;">
<div style="flex:1;height:3px;background:${trackColor};border-radius:2px;overflow:hidden;">
<div style="height:100%;width:${pct}%;background:${fillColor};border-radius:2px;"></div>
</div>
<span style="font-size:11px;color:${labelColor};font-weight:500;">${index + 1}/${total}</span>
</div>`;
}BRAND_PRIMARY((slideIndex + 1) / totalSlides) * 100%rgba(0,0,0,0.08)BRAND_PRIMARYrgba(0,0,0,0.3)rgba(255,255,255,0.12)#fffrgba(255,255,255,0.4)function progressBar(index, total, isLightSlide) {
const pct = ((index + 1) / total) * 100;
const trackColor = isLightSlide ? 'rgba(0,0,0,0.08)' : 'rgba(255,255,255,0.12)';
const fillColor = isLightSlide ? BRAND_PRIMARY : '#fff'; // 使用实际的BRAND_PRIMARY色值
const labelColor = isLightSlide ? 'rgba(0,0,0,0.3)' : 'rgba(255,255,255,0.4)';
return `<div style="position:absolute;bottom:0;left:0;right:0;padding:16px 28px 20px;z-index:10;display:flex;align-items:center;gap:10px;">
<div style="flex:1;height:3px;background:${trackColor};border-radius:2px;overflow:hidden;">
<div style="height:100%;width:${pct}%;background:${fillColor};border-radius:2px;"></div>
</div>
<span style="font-size:11px;color:${labelColor};font-weight:500;">${index + 1}/${total}</span>
</div>`;
}BRAND_PRIMARYrgba(0,0,0,0.06)rgba(0,0,0,0.25)rgba(255,255,255,0.08)rgba(255,255,255,0.35)function swipeArrow(isLightSlide) {
const bg = isLightSlide ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.08)';
const stroke = isLightSlide ? 'rgba(0,0,0,0.25)' : 'rgba(255,255,255,0.35)';
return `<div style="position:absolute;right:0;top:0;bottom:0;width:48px;z-index:9;display:flex;align-items:center;justify-content:center;background:linear-gradient(to right,transparent,${bg});">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 6l6 6-6 6" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>`;
}rgba(0,0,0,0.06)rgba(0,0,0,0.25)rgba(255,255,255,0.08)rgba(255,255,255,0.35)function swipeArrow(isLightSlide) {
const bg = isLightSlide ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.08)';
const stroke = isLightSlide ? 'rgba(0,0,0,0.25)' : 'rgba(255,255,255,0.35)';
return `<div style="position:absolute;right:0;top:0;bottom:0;width:48px;z-index:9;display:flex;align-items:center;justify-content:center;background:linear-gradient(to right,transparent,${bg});">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 6l6 6-6 6" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>`;
}<span style="font-size:11px;padding:5px 12px;border:1px solid rgba(255,255,255,0.1);border-radius:20px;color:#6B6560;text-decoration:line-through;">{Old tool}</span><span style="font-size:11px;padding:5px 12px;border:1px solid rgba(255,255,255,0.1);border-radius:20px;color:#6B6560;text-decoration:line-through;">{旧工具}</span><span style="font-size:11px;padding:5px 12px;background:rgba(255,255,255,0.06);border-radius:20px;color:{BRAND_LIGHT};">{Label}</span><span style="font-size:11px;padding:5px 12px;background:rgba(255,255,255,0.06);border-radius:20px;color:{BRAND_LIGHT};">{标签}</span><div style="padding:16px;background:rgba(0,0,0,0.15);border-radius:12px;border:1px solid rgba(255,255,255,0.08);">
<p class="sans" style="font-size:13px;color:rgba(255,255,255,0.5);margin-bottom:6px;">{Label}</p>
<p class="serif" style="font-size:15px;color:#fff;font-style:italic;line-height:1.4;">"{Quote text}"</p>
</div><div style="padding:16px;background:rgba(0,0,0,0.15);border-radius:12px;border:1px solid rgba(255,255,255,0.08);">
<p class="sans" style="font-size:13px;color:rgba(255,255,255,0.5);margin-bottom:6px;">{标签}</p>
<p class="serif" style="font-size:15px;color:#fff;font-style:italic;line-height:1.4;">"{引用文本}"</p>
</div><div style="display:flex;align-items:flex-start;gap:14px;padding:10px 0;border-bottom:1px solid {LIGHT_BORDER};">
<span style="color:{BRAND_PRIMARY};font-size:15px;width:18px;text-align:center;">{icon}</span>
<div>
<span class="sans" style="font-size:14px;font-weight:600;color:{DARK_BG};">{Label}</span>
<span class="sans" style="font-size:12px;color:#8A8580;">{Description}</span>
</div>
</div><div style="display:flex;align-items:flex-start;gap:14px;padding:10px 0;border-bottom:1px solid {LIGHT_BORDER};">
<span style="color:{BRAND_PRIMARY};font-size:15px;width:18px;text-align:center;">{图标}</span>
<div>
<span class="sans" style="font-size:14px;font-weight:600;color:{DARK_BG};">{标签}</span>
<span class="sans" style="font-size:12px;color:#8A8580;">{描述}</span>
</div>
</div><div style="display:flex;align-items:flex-start;gap:16px;padding:14px 0;border-bottom:1px solid {LIGHT_BORDER};">
<span class="serif" style="font-size:26px;font-weight:300;color:{BRAND_PRIMARY};min-width:34px;line-height:1;">01</span>
<div>
<span class="sans" style="font-size:14px;font-weight:600;color:{DARK_BG};">{Step title}</span>
<span class="sans" style="font-size:12px;color:#8A8580;">{Step description}</span>
</div>
</div><div style="display:flex;align-items:flex-start;gap:16px;padding:14px 0;border-bottom:1px solid {LIGHT_BORDER};">
<span class="serif" style="font-size:26px;font-weight:300;color:{BRAND_PRIMARY};min-width:34px;line-height:1;">01</span>
<div>
<span class="sans" style="font-size:14px;font-weight:600;color:{DARK_BG};">{步骤标题}</span>
<span class="sans" style="font-size:12px;color:#8A8580;">{步骤描述}</span>
</div>
</div><div style="width:32px;height:32px;border-radius:8px;background:{color};border:1px solid rgba(255,255,255,0.08);"></div><div style="width:32px;height:32px;border-radius:8px;background:{color};border:1px solid rgba(255,255,255,0.08);"></div><div style="display:inline-flex;align-items:center;gap:8px;padding:12px 28px;background:{LIGHT_BG};color:{BRAND_DARK};font-family:'{BODY_FONT}',sans-serif;font-weight:600;font-size:14px;border-radius:28px;">
{CTA text}
</div><div style="display:inline-flex;align-items:center;gap:8px;padding:12px 28px;background:{LIGHT_BG};color:{BRAND_DARK};font-family:'{BODY_FONT}',sans-serif;font-weight:600;font-size:14px;border-radius:28px;">
{CTA文本}
</div><span class="sans" style="display:inline-block;font-size:10px;font-weight:600;letter-spacing:2px;color:{color};margin-bottom:16px;">{TAG TEXT}</span>BRAND_PRIMARYBRAND_LIGHTrgba(255,255,255,0.6)<span class="sans" style="display:inline-block;font-size:10px;font-weight:600;letter-spacing:2px;color:{color};margin-bottom:16px;">{标签文本}</span>BRAND_PRIMARYBRAND_LIGHTrgba(255,255,255,0.6)0 36px0 36px 52pxjustify-content: centerjustify-content: flex-endpadding-bottom: 52px0 36px0 36px 52pxjustify-content: centerjustify-content: flex-endpadding-bottom: 52px.ig-frame.ig-framePath.write_text()open().write()data:image/jpeg;base64,...file.pngdevice_scale_factorPath.write_text()open().write()data:image/jpeg;base64,...file.pngdevice_scale_factorpython3 -c "import playwright" 2>/dev/null || pip3 install playwright
python3 -c "from playwright.sync_api import sync_playwright; sync_playwright().__enter__().chromium" 2>/dev/null || python3 -m playwright install chromiumpython3 -c "import playwright" 2>/dev/null || pip3 install playwright
python3 -c "from playwright.sync_api import sync_playwright; sync_playwright().__enter__().chromium" 2>/dev/null || python3 -m playwright install chromiumimport asyncio
from pathlib import Path
from playwright.async_api import async_playwright
INPUT_HTML = Path("/path/to/carousel.html")
OUTPUT_DIR = Path("/path/to/output/slides")
OUTPUT_DIR.mkdir(exist_ok=True)
TOTAL_SLIDES = 7 # Update to match your carousel
VIEW_W = 420
VIEW_H = 525
SCALE = 1080 / 420 # = 2.5714...
async def export_slides():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(
viewport={"width": VIEW_W, "height": VIEW_H},
device_scale_factor=SCALE,
)
html_content = INPUT_HTML.read_text(encoding="utf-8")
await page.set_content(html_content, wait_until="networkidle")
await page.wait_for_timeout(3000) # Wait for Google Fonts to load
# Hide IG frame chrome, show only the slide viewport
await page.evaluate("""() => {
document.querySelectorAll('.ig-header,.ig-dots,.ig-actions,.ig-caption')
.forEach(el => el.style.display='none');
const frame = document.querySelector('.ig-frame');
frame.style.cssText = 'width:420px;height:525px;max-width:none;border-radius:0;box-shadow:none;overflow:hidden;margin:0;';
const viewport = document.querySelector('.carousel-viewport');
viewport.style.cssText = 'width:420px;height:525px;aspect-ratio:unset;overflow:hidden;cursor:default;';
document.body.style.cssText = 'padding:0;margin:0;display:block;overflow:hidden;';
}""")
await page.wait_for_timeout(500)
for i in range(TOTAL_SLIDES):
await page.evaluate("""(idx) => {
const track = document.querySelector('.carousel-track');
track.style.transition = 'none';
track.style.transform = 'translateX(' + (-idx * 420) + 'px)';
}""", i)
await page.wait_for_timeout(400)
await page.screenshot(
path=str(OUTPUT_DIR / f"slide_{i+1}.png"),
clip={"x": 0, "y": 0, "width": VIEW_W, "height": VIEW_H}
)
print(f"Exported slide {i+1}/{TOTAL_SLIDES}")
await browser.close()
asyncio.run(export_slides())import asyncio
from pathlib import Path
from playwright.async_api import async_playwright
INPUT_HTML = Path("/path/to/carousel.html")
OUTPUT_DIR = Path("/path/to/output/slides")
OUTPUT_DIR.mkdir(exist_ok=True)
TOTAL_SLIDES = 7 # 根据你的轮播图更新此数值
VIEW_W = 420
VIEW_H = 525
SCALE = 1080 / 420 # = 2.5714...
async def export_slides():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(
viewport={"width": VIEW_W, "height": VIEW_H},
device_scale_factor=SCALE,
)
html_content = INPUT_HTML.read_text(encoding="utf-8")
await page.set_content(html_content, wait_until="networkidle")
await page.wait_for_timeout(3000) # 等待Google Fonts加载
# 隐藏IG框架的chrome元素,仅显示幻灯片视口
await page.evaluate("""() => {
document.querySelectorAll('.ig-header,.ig-dots,.ig-actions,.ig-caption')
.forEach(el => el.style.display='none');
const frame = document.querySelector('.ig-frame');
frame.style.cssText = 'width:420px;height:525px;max-width:none;border-radius:0;box-shadow:none;overflow:hidden;margin:0;';
const viewport = document.querySelector('.carousel-viewport');
viewport.style.cssText = 'width:420px;height:525px;aspect-ratio:unset;overflow:hidden;cursor:default;';
document.body.style.cssText = 'padding:0;margin:0;display:block;overflow:hidden;';
}""")
await page.wait_for_timeout(500)
for i in range(TOTAL_SLIDES):
await page.evaluate("""(idx) => {
const track = document.querySelector('.carousel-track');
track.style.transition = 'none';
track.style.transform = 'translateX(' + (-idx * 420) + 'px)';
}""", i)
await page.wait_for_timeout(400)
await page.screenshot(
path=str(OUTPUT_DIR / f"slide_{i+1}.png"),
clip={"x": 0, "y": 0, "width": VIEW_W, "height": VIEW_H}
)
print(f"已导出幻灯片 {i+1}/{TOTAL_SLIDES}")
await browser.close()
asyncio.run(export_slides())device_scale_factor=2.5714clipwait_for_timeout(3000)track.style.transition = 'none'device_scale_factor=2.5714clipwait_for_timeout(3000)track.style.transition = 'none'| Mistake | What goes wrong | Fix |
|---|---|---|
| Setting viewport to 1080×1350 | Layout reflows — fonts tiny, spacing breaks | Keep viewport at 420×525, use |
| Using shell scripts to generate HTML | | Always use Python for HTML generation |
| Not waiting for fonts | Headings render in fallback system fonts | |
| Not hiding IG frame chrome | Export includes header, dots, caption | Hide |
Changing | Entire layout shifts | Always keep at exactly 420px |
Leaving | Color renders as invalid / invisible | Always interpolate actual hex values into HTML |
| 错误 | 问题后果 | 修复方案 |
|---|---|---|
| 将视口设置为1080×1350 | 布局重排 —— 字体过小,间距错乱 | 保持视口为420×525,使用 |
| 使用Shell脚本生成HTML | | 始终使用Python生成HTML |
| 未等待字体加载 | 标题回退到系统默认字体 | 页面加载后执行 |
| 未隐藏IG框架chrome元素 | 导出内容包含头部、指示器、文案 | 隐藏 |
修改 | 整个布局偏移 | 始终保持宽度为精确的420px |
在CSS中保留 | 颜色渲染无效/不可见 | 始终将实际十六进制色值插入HTML |