Skip to main content

pdf-to-landing

Patrón completo para crear una webapp que convierte PDFs de propuestas de diseño en landing pages generadas por IA — extracción de texto, análisis de diseño, generación HTML, descarga directa o deploy.

설치로 이동

소스 정보

저장소
Ntizar/MasterMind
최근 소스 활동
2026년 9월 4일 10:27
감지된 SKILL.md 언어
스페인어
스타
2
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
6 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
pdf-to-landing
description
Patrón completo para crear una webapp que convierte PDFs de propuestas de diseño en landing pages generadas por IA — extracción de texto, análisis de diseño, generación HTML, descarga directa o deploy.
version
4.0.0
tags
["pdf","landing","ia","nan-builders","express","frontend"]
# PDF-to-Landing — Generador de landing pages desde PDFs ## Descripción Webapp completa que permite subir un PDF con una propuesta de diseño web y genera automáticamente una landing page HTML basada en el análisis del diseño, usando la API de NaN (qwen3.6). ## Arquitectura (v2 — 2 endpoints limpios) **⚠️ NO usar un endpoint monolítico** que haga todo (extracción + análisis + generación + deploy). El frontend acabaría haciendo múltiples POST al mismo endpoint con resultados inesperados. Usar 2 endpoints separados: ``` ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Subir PDF │───▶│ POST │───▶│ Análisis │ │ (web UI) │ │ /api/analyze│ │ (LLM NaN) │ └─────────────┘ └──────────────┘ └──────┬───────┘ │ design JSON ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Descargar │◀───│ POST │◀───│ Usuario │ │ HTML │ │ /api/generate│ │ confirma │ └─────────────┘ └──────────────┘ └──────────────┘ ``` **Flujo UX recomendado:** 1. Usuario sube PDF → `POST /api/analyze` → muestra diseño detectado (empresa, colores, sector, tono) 2. Usuario revisa y pulsa "Generar" → `POST /api/generate` → genera HTML 3. Usuario descarga HTML o abre en nueva pestaña **Por qué 2 endpoints:** - El usuario puede revisar el análisis ANTES de gastar tokens en generación - Si el análisis está mal, puede cancelar sin perder nada - El frontend no necesita estado complejo para sincronizar pasos - Los errores son claros: "error analizando" vs "error generando" ## Arquitectura v3 — Extracción visual + IA con colores reales **Problema v2:** La IA "adivinaba" colores del PDF porque solo recibía texto. Los colores generados no correspondían a la identidad visual real. **Solución v3:** Extraer colores del PDF visualmente (renderizar a canvas → Median Cut) y enviarlos a la IA como datos reales. ``` ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Subir PDF │───▶│ pdf.js │───▶│ Median Cut │ │ (browser) │ │ renderiza │ │ extrae │ └─────────────┘ │ primera │ │ colores │ │ página │ └──────┬───────┘ └──────────────┘ │ colores hex ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Subir PDF │───▶│ POST │───▶│ IA usa │ │ + colores │ │ /api/analyze│ │ colores │ │ detectados │ │ (colores + │ │ REALES │ └─────────────┘ │ texto) │ └──────┬───────┘ └──────────────┘ │ design JSON ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Descargar │◀───│ POST │◀───│ Usuario │ │ HTML │ │ /api/generate│ │ confirma │ └─────────────┘ └──────────────┘ └──────────────┘ ``` ## Pasos ### 1. Extracción visual de colores (CLIENT-SIDE, v3+) - **pdf.js** renderiza **hasta 5 páginas** del PDF a canvas en el navegador (no solo la primera) - **Median Cut algorithm** extrae los colores dominantes de los píxeles de TODAS las páginas combinadas - **⚠️ NO FILTRAR colores** — no eliminar blancos/negros/desaturados. El usuario decide cuáles usar. El filtro de saturación eliminaba verdes y pastel de diseños reales. - **20 buckets** en Median Cut para máxima cobertura (menos = pierde colores minoritarios pero importantes) - **Deduplicación euclídea** (threshold 30): elimina colores visualmente idénticos sin perder variedad. Sin esto, el mismo verde aparece 3-4 veces con hex casi iguales - **Muestreo cada 2px** (no 4px) para mayor precisión en la detección - Muestra los colores detectados como **chips editables** al usuario ANTES de analizar: - Clic en el dot → selector de color nativo del navegador - Clic en el hex → editar manualmente - ✕ → eliminar un color - **+ Añadir** → agregar colores manualmente (para correcciones) - **Por qué multi-página:** Un PDF puede tener colores de marca solo en páginas interiores. Renderizar solo la página 1 pierde esos colores. Con 5 páginas, se capturan todos. - **Por qué 15 buckets sin filtro:** Un PDF con 70% blanco, 20% verde marca, 5% gris texto, 5% otros → con 6 buckets y filtro, el verde se pierde. Con 15 sin filtro, aparece. - Ver `references/color-extraction-median-cut.md` para implementación completa **Por qué client-side:** No necesita servidor pesado, feedback instantáneo, y el usuario ve los colores antes de gastar tokens de IA. ### 2. Extracción de texto (SERVER-SIDE) - Usar `pdf-parse` v1.1.4 para extraer texto del PDF - **⚠️ Fijar versión `"1.1.4"` en package.json** — v2.x cambia la API completamente. Ver `references/pdf-parse-compatibility.md` para detalles. - **Pitfall ESM:** `pdf-parse` no tiene default export. Usar `createRequire`: ```javascript import { createRequire } from 'module'; const require = createRequire(import.meta.url); const pdfParse = require('pdf-parse'); ``` ### 3. Análisis de diseño con IA (mejorado en v3+) - Enviar texto extraído **+ colores detectados visualmente** a qwen3.6 vía NaN API - System prompt incluye: "Estos son los colores REALES extraidos visualmente del PDF. DEBES usarlos como base para la paleta." - **10 reglas críticas para colores** en el prompt: 1. `"fondo"` = color de fondo REAL del PDF (blanco, crema, oscuro, etc.) 2. `"texto"` = color del texto PRINCIPAL (negro, gris oscuro, etc.) 3. `"colores_dominantes"` = mínimo 5 colores (marca, fondo, texto, acentos) 4. Si el PDF tiene verde como marca, el primario DEBE ser verde 5. Fondo y texto son TAN IMPORTANTES como el color de marca - JSON con: empresa, sector, tono, paleta de colores (usando los hex reales), tipografía, secciones, estilo visual, inspiración, CTA, features, testimonios - **⚠️ Diseño detectado COMPLETAMENTE EDITABLE (v5+):** - Todos los campos son editables: Empresa (text), Sector (text), Tono (select), Inspiración (text), Tipografía (select), Estilo (textarea) - Los colores de paleta son editables con color picker + input hex - Los cambios del usuario se reflejan en `currentDesign` ANTES de generar - **Por qué editable:** La IA a menudo no capta el color exacto (ej: detecta `#ffcfb5` en vez de naranja quemado). El usuario es la última autoridad sobre su identidad visual. - UI: título dice "Diseño detectado (edita antes de generar)" - **Pitfall:** qwen3 envuelve razonamiento en tags `<think>...</think>`. Usar `indexOf`/`substring` en vez de regex para limpiar (evita problemas de escaping con `write_file`). - **Pitfall prompt colores:** Sin instrucciones explícitas sobre fondo/texto, la IA siempre pone `fondo: #FFFFFF` y `texto: #333333` por defecto aunque el PDF sea oscuro. ### 4. Generación de HTML (mejorado en v3+) - Enviar diseño analizado a qwen3.6 con **bloque de强制 de colores**: ``` COLOLES OBLIGATORIOS (extraidos del PDF real, NO los cambies): - primario: #2E7D32 (extraido del pixel real del PDF) - secundario: #4CAF50 - acento: #81C784 - fondo: #FFFFFF - texto: #000000 Todos los colores del PDF: #2E7D32, #4CAF50, #81C784, #C8E6C9, #1B5E20 ``` - El prompt DEBE especificar: "Usa estos colores EXACTAMENTE en las variables CSS y en TODOS los elementos. NUNCA uses colores por defecto azul/naranja." - **10 reglas de generación:** 1. Responsive (móvil + desktop) 2. Moderna (glassmorphism, gradientes sutiles, animaciones CSS) 3. Un solo archivo HTML con todo inline (CSS + JS) 4. Google Fonts: Inter para body, Space Grotesk para headings 5. Footer: "Hecho con ❤️ por David Antizar" 6. USAR LOS COLORES EXACTOS de la paleta 7. Hero + servicios + sobre nosotros + testimonios + CTA + footer 8. NO frameworks — HTML + CSS + JS vanilla 9. Animaciones fade-in al scroll con IntersectionObserver 10. CSS variables con los colores exactos - Landing page responsive, moderna, con glassmorphism - Footer: "Hecho con ❤️ por David Antizar" - Un solo archivo HTML con CSS y JS inline ### 5. Frontend — Three.js + Glassmorphism - **Three.js** para fondo animado: partículas + formas geométricas wireframe + parallax con ratón - **Liquid glass** para cards: `backdrop-filter: blur(24px) saturate(180%)`, gradientes sutiles, specular highlight - Colores del fondo adaptados a la paleta del proyecto (azul/naranja por defecto) - Ver `references/threejs-particle-background.md` para template reutilizable ### 6. Deploy (OPCIONAL — según prefiera el usuario) - **Opción A (recomendada):** Descarga directa del HTML — sin dependencia de GitHub - **Opción B:** GitHub Pages (solo frontend estático) - **Opción C:** NaN Builders (backend + frontend con Dockerfile) - **Preguntar al usuario** qué prefiere ANTES de implementar deploy automático ## Estructura del proyecto ``` pdf-to-landing/ ├── server.js # Backend Express (upload, extracción, IA) ├── public/ │ └── index.html # Frontend: Three.js bg + pdf.js + Median Cut + glass UI ├── package.json # type: "module" OBLIGATORIO ├── Dockerfile # Multi-stage, non-root user ├── .env # NAN_API token (NO en Git) ├── .env.example # Template (SÍ en Git) ├── .gitignore # Incluye .env └── .dockerignore # NO incluye .env ``` ### Variables CSS de la paleta detectada El frontend detecta colores y los envía al backend. El backend los incluye en el prompt de IA. El HTML generado usa CSS variables con esos colores exactos: ```css :root { --primary: #F7931A; /* color dominante del PDF */ --secondary: #2C3E50; /* segundo color */ --accent: #1ABC9C; /* color llamativo */ --bg: #FFFFFF; /* fondo (siempre claro) */ --text: #333333; /* texto oscuro */ } ``` ## Deploy en NaN Builders ### Dockerfile mínimo ```dockerfile FROM node:20-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=dev FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production COPY --from=deps /app/node_modules ./node_modules COPY package.json ./ COPY server.js ./ COPY public/ ./public/ RUN mkdir -p uploads deploy RUN addgroup -S appgroup && adduser -S appuser -G appgroup RUN chown -R appuser:appgroup /app USER appuser HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget -qO- http://localhost:3000/healthz || exit 1 EXPOSE 3000 CMD ["node", "server.js"] ``` ### Checklist de deploy - [ ] `package.json` tiene `"type": "module"` si usa ESM - [ ] `.gitignore` incluye `.env` - [ ] `.dockerignore` NO incluye `.env` (se copia al contenedor) - [ ] Dockerfile tiene `USER appuser` (requisito NaN) - [ ] Dockerfile tiene `HEALTHCHECK` apuntando a `/healthz` - [ ] EXPOSE coincide con container port en NaN - [ ] Server.js usa `process.env.PORT || 3000` - [ ] `.env` tiene `NAN_API=<token_real>` (no literal `${NAN_API}`) ### Diagnóstico de deploy fallido | Síntoma | Causa | Solución | |---------|-------|----------| | Build Kaniko SUCCEEDED, URL 404 Cloudflare | Contenedor crash al arrancar | Ver logs en cloud.nan.builders | | Build Kaniko SUCCEEDED, URL 502 | Contenedor crash | Verificar ESM/CJS, token, puerto | | `import` falla en server.js | package.json sin `"type": "module"` | Añadir `"type": "module"` | | `pdf-parse` no tiene default export | ESM sin createRequire | Usar `createRequire(import.meta.url)` | | `.env` tiene literal `${NAN_API}` | Variable no expandida | `source /hermes-home/.env && echo "NAN_API=$NAN_API" > .env` | ## Frontend — Visualización del proceso (v5) El frontend muestra el flujo en 5 fases: 1. 📄 **Upload + Preview** — drag & drop, renderiza hasta 5 páginas con pdf.js 2. 🎨 **Colores detectados** — chips hex extraídos visualmente del PDF (Median Cut, 15 buckets, sin filtro) 3. 🔍 **Análisis IA** — envía PDF + colores al backend, muestra diseño detectado 4. ✏️ **Editar diseño** — TODOS los campos editables (empresa, sector, tono, tipografía, estilo, colores) 5. 🚀 **Generar + Descargar** — usuario decide cuándo generar, descarga HTML o vista previa **UX clave:** Mostrar colores y análisis ANTES de generar. El usuario ve qué detectó la IA y puede: - Editar colores antes de analizar (chips editables) - Editar diseño detectado después del análisis (campos editables) - Cancelar si está mal **Three.js background:** Partículas + formas geométricas wireframe con parallax. Colores adaptados a la paleta del proyecto. ## Pitfalls - **🔴 `const` usado antes de definirlo → ReferenceError (TDZ):** En ESM, `const upload = multer(...)` definido DESPUÉS de `app.post('/api/analyze', upload.single(...), handler)` causa crash inmediato al arrancar. El orden en server.js DEBE ser: (1) imports, (2) `const` de multer/storage/upload, (3) funciones auxiliares, (4) handlers, (5) `app.post()` con rutas, (6) `express.static`, (7) `app.listen()`. Ver `references/server-2-endpoint-pattern.md` para el orden completo. - **🔴 `write_file` doble-escapa backslashes en regex:** Cuando se escribe código con `write_file` que contiene regex como `new RegExp('[\\s\\S]*?')`, el tool puede duplicar los backslashes (`[\\\\s\\\\S]`). El regex queda roto. **Solución:** Usar `indexOf` en vez de regex para patrones simples (ej: limpiar `<think>` tags con `indexOf`/`substring` en vez de RegExp). Si se necesita regex, verificar con `cat -n file.js | grep 'backslash'` tras escribir. - **🔴 Endpoint monolítico:** NO poner extracción + análisis + generación + deploy en un solo `POST /api/process`. El frontend hace múltiples calls y recibe resultados parciales o duplicados. Usar 2 endpoints separados: `/api/analyze` y `/api/generate`. - **🔴 `pdf-parse` v2.x rompe la API de v1:** Desde v2.0.0, `pdf-parse` cambió completamente su API. El paquete ya NO es una función directa. Esto causa `TypeError: pdfParse is not a function` al subir un PDF. - **v1 (compatible):** `const pdfParse = require('pdf-parse'); const data = await pdfParse(buffer);` - **v2 (NO compatible):** `const { PDFParse } = require('pdf-parse'); const parser = new PDFParse({ data: buffer }); const result = await parser.getText(); await parser.destroy();` - **Solución:** Fijar versión: `"pdf-parse": "1.1.4"` en package.json. Si `npm install` instala v2.x, hacer `npm install pdf-parse@1.1.4` explícitamente. - **Cuidado:** `npm ci` sin lockfile fija puede instalar v2.x. Verificar siempre con `npm list pdf-parse` tras instalar. - **Si se necesita v2:** Reescribir el handler: importar `{ PDFParse }`, crear instancia con `new PDFParse({ data })`, llamar `getText()`, y `destroy()` al final. - **🔴 `pdf-parse` en ESM:** No tiene default export. Usar `createRequire`. - **🔴 `package.json` sin `"type": "module"`:** Server.js con `import` crash en contenedor. - **🔴 `.env` en `.dockerignore`:** El token NAN_API no llega al contenedor. Quitarlo. - **🔴 `.env` con literal `${VAR}`:** Verificar que contiene el token real, no el literal. - **🔴 `express.static` captura rutas:** Si hay un archivo estático que coincide con una ruta API, sirve el archivo en vez de la ruta. Definir rutas API ANTES de `express.static`. - **⚠️ NaN no hereda env vars:** El contenedor necesita `.env` copiado o variables configuradas en el dashboard NaN. - **⚠️ Build Kaniko exitoso ≠ deploy exitoso:** El build puede pasar pero el contenedor crashar al arrancar. Verificar logs del contenedor, no solo del build. - **⚠️ Deploy automático no siempre funciona:** NaNBuilders puede no tener auto-deploy configurado. Preguntar al usuario si quiere deploy o solo descarga directa. - **⚠️ Colores genéricos vs reales:** Sin extracción visual de colores, la IA siempre genera azul/naranja por defecto. La extracción Median Cut del canvas del PDF es la única forma de que la IA use los colores reales de la marca. - **🔴 Median Cut con filtro elimina colores reales:** El algoritmo original filtraba: `brightness > 240` (blancos), `brightness < 15` (negros), `saturation < 0.08` (grises). Esto ELIMINABA verdes, pastels, y colores desaturados que son legítimos en diseños reales. **Solución:** NO FILTRAR. Devolver todos los colores de los 15 buckets y dejar al usuario decidir cuáles usar. El filtro de saturación es el más dañino — un verde claro como `#81C784` tiene saturación ~0.35 y se filtraba. - **⚠️ pdf-parse v1.1.1 funciona igual que v1.1.4:** Ambas versiones v1.x usan la misma API (`require('pdf-parse')` como función). No hay necesidad de forzar 1.1.4 específicamente — cualquier v1.x sirve. Lo crítico es NO usar v2.x. - **⚠️ NaNBuilders .env no se hereda:** Las variables de entorno del host NO están disponibles en el contenedor Docker. El `.env` debe copiarse al contenedor (via `COPY .env .` en Dockerfile) O configurarse en el dashboard NaN (pestaña Env). Si el `.env` está en `.gitignore`, no llega al contenedor por el repo — hay que usar el dashboard NaN. - **⚠️ `write_file` y regex:** `write_file` puede doble-escapar backslashes en regex. Si necesitas limpiar `<think>` tags, usa `indexOf`/`substring` en vez de RegExp. Ejemplo: ```javascript // ❌ Regex — puede fallar con doble-escaping
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기