# job-carousel — Usage Guide

Transform a resume + job description into a polished LinkedIn career carousel package in minutes.

---

## Prerequisites

- **Node.js 18+** (check: `node --version`)
- **npm** (check: `npm --version`)
- Install dependencies:
  ```bash
  npm install
  ```
- Puppeteer downloads Chromium automatically on first install. If it fails, see Troubleshooting.

---

## Quick Start

```bash
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/my_resume.txt \
  --jd ./inputs/target_jd.txt
```

This runs the full pipeline with defaults (7 slides, clean theme, recruiter mode auto-inferred).

Output goes to `output/job-carousel/`.

---

## All CLI Flags

| Flag | Required | Default | Description |
|------|----------|---------|-------------|
| `--resume <path>` | Yes | — | Path to resume file (`.txt`, `.md`, or plain text) |
| `--jd <path>` | Yes | — | Path to job description file |
| `--mode <mode>` | No | `auto` | Positioning mode: `recruiter`, `hiring-manager`, `personal-brand` |
| `--theme <theme>` | No | `clean` | Visual theme: `clean`, `dark`, `bold` |
| `--slides <n>` | No | `7` | Number of slides: `6`, `7`, or `8` |
| `--output <path>` | No | `./output/job-carousel` | Output directory |
| `--export <targets>` | No | — | Additional exports: `canva`, `figma`, or `canva,figma` |
| `--no-render` | No | false | Skip PNG/PDF export (produces JSON + caption only) |

### Examples

```bash
# Full run with explicit mode and theme
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume_jane.txt \
  --jd ./inputs/stripe_jd.txt \
  --mode recruiter \
  --theme clean \
  --slides 7

# Hiring manager mode, dark theme, 8 slides
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume_alex.txt \
  --jd ./inputs/plaid_pm_jd.txt \
  --mode hiring-manager \
  --theme dark \
  --slides 8

# Export to Canva (PPTX) only
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume_jane.txt \
  --jd ./inputs/stripe_jd.txt \
  --export canva

# Export to Figma (SVG) only
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume_jane.txt \
  --jd ./inputs/stripe_jd.txt \
  --export figma

# Export PDF + PPTX + SVG all at once
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume_jane.txt \
  --jd ./inputs/stripe_jd.txt \
  --export canva,figma

# Fast run without visual export (JSON + caption only)
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume_sam.txt \
  --jd ./inputs/startup_se_jd.txt \
  --no-render
```

### npm shortcut

After `npm install`, you can also use:
```bash
npm run carousel -- --resume ./inputs/resume.txt --jd ./inputs/jd.txt
```

---

## Output File Descriptions

All output files are written to `--output` (default: `output/job-carousel/`):

| File | Description | Flag required |
|------|-------------|---------------|
| `linkedin_carousel.pdf` | Primary deliverable — multi-page PDF, upload-ready | default |
| `linkedin_carousel.pptx` | Canva-ready presentation — all text and shapes are editable | `--export canva` |
| `slides_preview/slide_01.png` … | Individual PNG per slide at 1080×1080 | default |
| `figma_export/slide_01.svg` … | Figma-ready SVG per slide — editable vectors and live text | `--export figma` |
| `figma_export/FIGMA_IMPORT_GUIDE.md` | Step-by-step Figma import instructions | `--export figma` |
| `linkedin_caption.txt` | 3 caption variants with CTAs and hashtags | default |
| `carousel.json` | Full structured document (all slide data, fit analysis, scores) | default |
| `carousel_manifest.json` | Metadata: inputs, outputs, positioning angle, top highlights | default |

---

## Positioning Modes

### `recruiter` (default for most roles)
- Optimizes for: clarity, fit signal, seniority, business impact
- Headline style: action + outcome, plain language, easy to skim
- Best for: standard job searches, LinkedIn applications, recruiter outreach
- Auto-selected when: JD is business-focused, mid-senior level, general industry

### `hiring-manager`
- Optimizes for: technical depth, methods, execution credibility, ownership
- Headline style: shows how they think, what hard problems they solve
- Best for: technical roles, staff/principal/director levels, engineering orgs
- Auto-selected when: JD has heavy technical content (architecture, distributed systems, ML)

### `personal-brand`
- Optimizes for: distinctiveness, memorability, social appeal
- Headline style: editorial, opinionated, feels like a person not a job title
- Best for: consultants, creators, speakers, career pivoters, personal brand builders
- Auto-selected when: resume has freelance/consulting/OSS/speaking signals

---

## Theme Options

### `clean` (default)
White/light background, navy accent, dark text. Clean, modern, professional. Works for any audience.

### `dark`
Dark (slate-900) background, gold accent, white text. Premium editorial feel. Best for senior/executive positioning.

### `bold`
Warm off-white background, orange accent, near-black text. Energetic and opinionated. Good for startup roles or personal-brand mode.

---

## Resume and JD Format

**Resume:**
- Plain text is fine — even messy formatting works
- The parser extracts: roles, employers, bullets, dates, tools, metrics, domains
- Paste in raw text from Word/PDF or provide a `.txt` / `.md` file
- For best results: include at least 3–5 achievement bullets with numbers or scope indicators

**Job Description:**
- Copy the full JD from the company's website or job board
- The parser extracts: title, seniority, required/preferred skills, domain clues, themes
- Minimum viable: job title + company + 3–5 key requirements

---

## Exporting to Canva

Canva can import PPTX files and convert them into fully editable designs.

### Step 1 — Generate the PPTX

```bash
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume.txt \
  --jd ./inputs/jd.txt \
  --export canva
```

This writes `output/job-carousel/linkedin_carousel.pptx`.

### Step 2 — Import into Canva

1. Open [canva.com](https://canva.com) and log in
2. Click **Create a design**
3. Click **Import file** (top-right of the template picker)
4. Select `linkedin_carousel.pptx`
5. Canva converts each slide into an editable Canva page

### What's editable in Canva

| Element | Editable in Canva |
|---|---|
| All headline and body text | Yes — click to edit |
| Background colors | Yes — click the background, change color |
| Accent bars and shapes | Yes — click to recolor or resize |
| Bullet cards | Yes — grouped shapes, ungroup to edit individually |
| Metric hero numbers | Yes — large text boxes |
| Fonts | Yes — select text, switch font in Canva's toolbar |

### Tips

- Use **Canva's brand kit** to replace the accent color with your personal brand color in one click
- Resize the design to portrait (1080×1350) for Instagram Stories-style carousels using Canva's "Resize" tool
- Export from Canva as **PNG at 2x** for highest quality LinkedIn upload

---

## Exporting to Figma

The pipeline generates one SVG file per slide. Figma imports SVGs natively as editable frames.

### Step 1 — Generate the SVGs

```bash
npx ts-node src/job-carousel/index.ts \
  --resume ./inputs/resume.txt \
  --jd ./inputs/jd.txt \
  --export figma
```

This writes `output/job-carousel/figma_export/slide_01.svg` … `slide_07.svg` plus a `FIGMA_IMPORT_GUIDE.md`.

### Step 2 — Import into Figma

**Option A — Drag and drop (fastest):**
1. Open Figma (web or desktop app)
2. Open your target file or create a new one
3. Select all `slide_*.svg` files in your file explorer
4. Drag them onto the Figma canvas
5. Each slide lands as a 1080×1080 frame

**Option B — File menu:**
1. Open Figma
2. Go to **File → Import**
3. Select all `slide_*.svg` files
4. Click **Open**

### What's editable in Figma

| Element | Editable in Figma |
|---|---|
| All text (titles, subtitles, body, bullets) | Yes — double-click to edit |
| Background fills | Yes — select the background rect, change fill |
| Accent bars, cards, circles | Yes — fully editable vector shapes |
| Colors | Yes — use Figma's color picker or swap via styles |
| Layout and spacing | Yes — move, resize, re-align anything |
| Typography | Yes — change font, weight, size per text layer |

### Recommended Figma workflow

1. Import all 7 slides as described above
2. Select all frames → right-click → **Group Selection** → name it `Career Carousel`
3. Use **Auto Layout** on the group to space slides evenly for the LinkedIn preview mockup
4. When done editing, select all frames → **Export** → PNG at **2x** (2160×2160)
5. Upload the PNGs directly to LinkedIn as a Document post

### Tips

- Create a **Figma Color Style** for the accent color — changing it once updates all slides
- Use **Components** to turn the card layout on slide 2 into a reusable component
- The `carousel.json` in the output folder contains all slide content — useful if you want to regenerate with different copy without re-running the full pipeline

---

## Troubleshooting

### Puppeteer / Chromium not downloading
```bash
# Manual Puppeteer browser install
npx puppeteer browsers install chrome

# Or skip render and use JSON output only
npx ts-node src/job-carousel/index.ts --resume ... --jd ... --no-render
```

### PDF render fails but PNGs work
Check that `pdf-lib` is installed:
```bash
npm install pdf-lib
```

### Fonts not rendering correctly in PNGs
Puppeteer uses Chromium's font rendering. If Google Fonts (Inter) can't be loaded (offline environment):
- The system falls back to Helvetica Neue / Arial
- Output quality will be similar but exact font won't match the design spec
- For offline use, bundle Inter as a base64 font in `renderSlides.tsx`

### TypeScript errors during `ts-node`
If you see TS errors in strict mode, try:
```bash
npx ts-node --transpile-only src/job-carousel/index.ts --resume ... --jd ...
```
The `--transpile-only` flag skips type checking for faster CLI execution.

### "No highlights scored above threshold" warning
This happens when the resume is very sparse or doesn't match the JD well. The carousel will still generate using narrative framing and capability signals. Consider:
- Adding more achievement bullets to the resume input
- Using `--mode personal-brand` for a less keyword-dependent approach

### Output files exist but PDF is empty
This can happen if Puppeteer timed out during rendering. Try:
```bash
# Increase Puppeteer timeout (modify exportPng.ts, networkidle0 timeout)
# Or use --no-render and view individual HTML by adding renderSlides output to index.ts
```

---

## Architecture Overview

```
resume text → parseResume() → CandidateProfile
jd text    → parseJobDescription() → TargetRoleProfile
                ↓
           analyzeFit() → FitAnalysis
                ↓
       rankCandidateHighlights() → HighlightCandidate[]
                ↓
       choosePositioningAngle() → PositioningAngle
                ↓
        buildCarouselOutline() → SlideOutline[]
                ↓
          writeSlideCopy() → SlideContent[]
                ↓
         renderSlideToHtml() → HTML strings
                ↓
     exportToPngs() + exportToPdf() → PNG + PDF        (always)
     exportToPptx()                 → PPTX for Canva   (--export canva)
     exportToSvgs()                 → SVG per slide     (--export figma)
                ↓
         packageOutputs() → JSON + caption + manifest
```

All intermediate data is typed with Zod schemas in `src/job-carousel/schema/carouselSchema.ts`.
