← Tools Portal
THE BLACKCAT Carousel Studio
# THE BLACKCAT Carousel Studio
## Purpose
Build Instagram carousels (1080x1350, 4:5) with pixel-perfect brand typography.
Write the placa texts, pick a brand, optionally upload background photos, press
one button, get the JPGs ready to post. The text is HTML/CSS rendered by Chrome
headless - never AI - so the brand typography is identical on every placa.
## Launch
```bash
scripts/open-carousel-studio.command
```
Then open `http://127.0.0.1:8781`.
## Usage
1. **Titulo / hook** - the giant first placa that stops the scroll.
2. **Placas** - one placa per paragraph (separate with a blank line). Inside a
placa, single line breaks split the lead line from the body/caption.
3. Pick a **brand** (BLACKCAT, NDC, or Neutral).
4. Optional: drag or click to **upload background photos** (JPG/PNG). They are
assigned to placas in order. No photo -> an elegant brand gradient background.
5. Press **"Generar carrusel"**. You get a grid of previews, a **ZIP** download,
and a **suggested photo prompt per placa** (Copy button) to generate images
in Higgsfield / Seedream and upload them.
Advanced: carousel name and the default type for the middle placas.
## Slide types (from SISTEMA.md)
The server decides the type by position and shape (or you set a default):
- **hook** (portada) - placa 1: giant display hero + italic sub + micro line.
- **texto** - text over photo/gradient, bottom-left anchor (title bold + body).
- **cita** - a quote in large italic with an accent quotation mark + attribution
(a last line starting with `-`).
- **lista** - short numbered list (accent numbers), 2+ `-`/`1.` lines auto-detect.
- **cierre** (CTA) - placa N: brand line + soft caption, large wordmark + handle.
Typography mixes the two brand roles word by word (italic personality + bold
display, SISTEMA.md Regla 2). Titles auto-fit their font size to avoid overflow.
## Brands
Three presets in `brands.py` (3-role palette + 2-role fonts + handle):
- **blackcat** - near-black `#1A1718` primary, warm-white `#F7F5F5` text, magenta
`#D90467` accent. Rubik (display) + Open Sans italic (personality).
Handle `@theblackcatmedia`.
- **ndc** - navy `#152864` primary, warm-white text, orange `#F8AA00` accent
(green/lightblue available). Montserrat. Handle `@NDCpartnership`. Follows the
NDC brand guide (real-people photography, no lens flare / suffering).
- **neutral** - warm charcoal + cream + terracotta, Inter + Fraunces italic.
## Engine
One full-page 1080x1350 HTML per placa -> Chrome headless screenshot ->
PNG -> JPEG via `sips`. No Node, no Puppeteer (architecture decision v1).
```
Google Chrome --headless=new --screenshot=out.png \
--window-size=1080,1350 --force-device-scale-factor=1 \
--allow-file-access-from-files --virtual-time-budget=5000 slide.html
sips -s format jpeg -s formatOptions 92 out.png --out placa.jpg
```
`--virtual-time-budget=5000` lets Google Fonts and the background photo settle
before the capture. `--force-device-scale-factor=1` + `--window-size=1080,1350`
gives an exact 1080x1350 PNG. `sips` (built into macOS) converts to JPEG, so
there is no Pillow dependency. Fonts load from Google Fonts in each slide's head.
## Endpoints
- `GET /api/health` - `{ chromeReady, brands }` (chromeReady = Chrome + sips found).
- `GET /api/brands` - brand list for the UI cards.
- `POST /api/upload` - multipart JPG/PNG background photos into `input/`
(cap 50 MB). Returns the stored filenames.
- `POST /api/generate` - `{ slides: [{text, photo?, type?}...], brand, title?,
outputName?, defaultType? }` -> async job. Rejects empty slides (400) and
unknown brands (400).
- `GET /api/status` - job snapshot (progress + step + placas + prompts).
- `GET /api/download/<slug>/<file>` - serve one generated placa JPG.
- `GET /api/download-zip/<slug>` - ZIP of all placas in a carousel.
- `GET /api/photo/<name>` - serve an uploaded photo (for the thumbnail preview).
- `POST /api/open-folder` - reveal the carousel's `output/` folder in Finder.
## Notes
- Uploaded photos (`input/`) and generated placas (`output/`) are git-ignored,
local only. Nothing is uploaded off this Mac.
- Requires Google Chrome at the standard `/Applications` path and macOS `sips`.
## Credit
The method - separate AI photo from programmatic typography, the 5-beat
narrative, the slide types, and the editorial photo-prompt pattern - is adapted
from **carrusel-ana-lab by Ana Paula Cascardo**, shared for community reuse. The
design rules live in that package's `SISTEMA.md` (13 reglas, tipos de slide,
formatos rotativos). This Studio reimplements the renderer in Python + Chrome
headless (no Node/Puppeteer) and wraps it in the One-Drop UI.