# 1.5.0 — import-captions keeps your caption style (--clone-style)

Minor release. `import-captions --clone-style` makes the engine **photocopy the look of your existing captions** (font, contour/stroke, shadow, size) onto the freshly injected word/keyword captions — and lay the highlight color on top. It is **font-agnostic**: whatever font you picked in CapCut survives, even one no preset library knows about. This closes the gap that kept `inject_word_captions.py` alive (the patcher preserved style by cloning a template caption).

## Highlights

- 🎨 **`import-captions … --clone-style`.** Before replacing the target track (`--track-name`, default `text`), the engine reads that track's **first existing caption**, photocopies its full style block (`font`/`strokes`/`shadows`/`size`/`bold`), and applies it to every new caption — overriding only the `range` (per span) and the `fill` (solid highlight color). The template material is deep-cloned, so material-level fields (`text_color`, `border_*`, `shadow_*`, `line_spacing`, …) are preserved too. Exactly the patcher's behavior, now in the engine.
- 🔤 **Font-agnostic.** We copy the style block verbatim — we never resolve or recognize the font. A font that isn't in any preset (CC-DerStil, etc.) is preserved just the same; the keyword color simply sits on it. Validated with a deliberately unknown font in a live CapCut render.
- 🧱 **Parity = every card becomes rich-text** in clone mode (like the patcher's `is_rich_text=True` on all cards), so contour/shadow survive on highlighted AND non-highlighted cards.
- 🛟 **Safe fallback.** Empty target track / unparseable caption content → falls back to the default style with a stderr warning, never an error.
- 🔒 **Opt-in, zero default change.** Without `--clone-style`, `import-captions` and `add-text` output is byte-for-byte identical to 1.4.0. `buildRichTextContent`'s lean default span shape is unchanged (locked by test).
- 🧪 **241 tracked tests** (235 prior + 6 clone-style), incl. "unknown font cloned onto every span, only range+fill change", span independence (no shared nested refs), and the empty-track fallback.

## CLI surface

```
capcut-david import-captions <project> <captions.json>
    [--highlight-color <hex>] [--track-name <name>] [--clone-style]
    # --clone-style: keep the target track's existing caption font/stroke/shadow.
    # captions.json = [{ "text", "start", "end", "hl"?: [s,e], "color"? }]   (start/end in µs)
```

## Typical flow (replaces inject_word_captions.py)

1. In CapCut, style one caption on your text track however you like (any font, contour, shadow).
2. `capcut-david import-captions <draft> captions-styled.json --track-name <your-track> --clone-style`
3. Reopen CapCut — all word captions now carry your style + the keyword colors.

## Migration

**No code changes required.** `--clone-style` is opt-in. Pipelines that called `inject_word_captions.py` can switch to `import-captions … --clone-style` (same `captions-styled.json` shape).

## Compatibility

- CapCut ≥ 5.x desktop (Windows + macOS), JianYing 6+ unsupported — unchanged.
- Node `>= 18` — unchanged.
- Runtime dependencies: zero — unchanged.
- Default path (no `--clone-style`) byte-identical to 1.4.0; clone mode is new and only active with the flag.

## Roadmap (1.x — non-binding)

Unchanged from 1.0.0:

- `1.x.0` — `capcut-david query` (animation / sticker / effect / filter catalogue lookup)
- `1.x.0` — `capcut-david validate <project>` (schema-invariant linter)
- `1.x.0` — JianYing 6+ research; `psycho-build` dynamic audio ducking
