# Extended Caption Styling: Font, Shadow, Dimmed Color **Date:** 2026-09-22 **Scope:** Add font selection (system font detection), drop shadow controls, dimmed/unspoken text color customization, and background toggle to the shared caption settings panel. ## Problem Several burn-in caption style properties are hardcoded: font is always Arial, shadow is always off, and the dimmed (unspoken) text color in word-by-word mode is a hardcoded grey (`&H73CCCCCC`) that doesn't relate to the user's chosen text color. The preview uses a different approach (CSS opacity on the main color), creating a visual mismatch between preview and exported captions. ## New Settings Fields Added to `CaptionSettings` (frontend) and `CaptionStyle` (backend): | Field | Type | Default | Description | |---|---|---|---| | `fontFamily` | string | `'Arial'` | Font family name from system fonts | | `backgroundEnabled` | boolean | `true` | Explicit toggle for the background box (replaces relying on opacity=0) | | `shadowEnabled` | boolean | `false` | Drop shadow toggle (mutually exclusive with background) | | `shadowDepth` | number (1-5) | `2` | Shadow offset in ASS units | | `shadowColor` | string | `'#000000'` | Shadow color (hex) | | `dimmedColorMode` | `'auto' \| 'custom'` | `'auto'` | How unspoken word color is derived | | `dimmedOpacity` | number (0.1-0.9) | `0.4` | Opacity for auto-derived dimmed color | | `dimmedColor` | string | `'#999999'` | Custom dimmed color (used in custom mode) | ## Background / Shadow Mutual Exclusivity Background and shadow are **mutually exclusive**. Enabling one disables the other. This is required because ASS uses `BackColour` for both the background box and shadow color — they cannot be independent simultaneously. ### ASS BorderStyle Mapping | Background | Outline | Shadow | ASS BorderStyle | ASS Outline | ASS Shadow | |---|---|---|---|---|---| | ON | ON | - | 4 | 2 | 0 | | ON | OFF | - | 3 | 0 | 0 | | - | ON | ON | 1 | 2 | depth | | - | OFF | ON | 1 | 0 | depth | | OFF | ON | OFF | 1 | 2 | 0 | | OFF | OFF | OFF | 1 | 0 | 0 | When **background is ON**: `BackColour` = `backgroundColor` + `backgroundOpacity` alpha. When **shadow is ON**: `BackColour` = `shadowColor` (fully opaque). When **neither**: `BackColour` = transparent (`&HFF000000`). ## Dimmed Text Color In word-by-word (karaoke) mode, unspoken words appear in a dimmed color (ASS `SecondaryColour`). ### Auto Mode (default) The dimmed color is derived from the main `textColor` at `dimmedOpacity`. Both preview and burn-in use the same approach: - **Preview CSS:** `opacity: {dimmedOpacity}` on unspoken word spans (current behavior, but using the configurable value instead of hardcoded 0.4) - **Burn-in ASS:** `SecondaryColour = hex_to_ass_color_with_alpha(textColor, (1 - dimmedOpacity) * 255)` ### Custom Mode The user picks a specific `dimmedColor`. Both preview and burn-in use it directly: - **Preview CSS:** `color: {dimmedColor}; opacity: 1` on unspoken word spans - **Burn-in ASS:** `SecondaryColour = hex_to_ass_color(dimmedColor)` ## System Font Detection A new Tauri command `list_system_fonts` runs `fc-list : family` (fontconfig, available because ffmpeg/libass depend on it). Output is parsed into a sorted, deduplicated list of font family names. - Called once when the caption settings panel opens; result is cached in component state. - If `fc-list` is not available, falls back to a hardcoded preset list: Arial, Helvetica, Verdana, Georgia, Times New Roman, Courier New, Impact. - The dropdown renders each option with `font-family` set to that font's name, providing a live preview of each font. ## Panel UI Layout The settings panel (320px wide) is organized into grouped sections: 1. **Font**: Dropdown (system fonts) + Size slider + Bold checkbox 2. **Text Color**: Color picker 3. **Dimmed Text**: Auto/Custom toggle. Auto: opacity slider. Custom: color picker. 4. **Outline**: Checkbox + color picker (disabled when unchecked) 5. **Background** (mutually exclusive with shadow): Checkbox + color picker + opacity slider 6. **Shadow** (mutually exclusive with background): Checkbox + depth slider + color picker 7. **Position**: Bottom/Top radio 8. **Word Highlight**: Checkbox (karaoke on/off) 9. **Reset to defaults** button ## Files Changed | File | Change | |---|---| | `src/lib/stores/preferences.svelte.ts` | Add new fields to `CaptionSettings` interface and defaults | | `src-tauri/src/models.rs` | Add new fields to `CaptionStyle` struct | | `src/lib/bindings/export.ts` | Update TS `CaptionStyle` interface | | `src/lib/components/ExportDialog.svelte` | Pass new fields through to export config | | `src-tauri/src/services/clip_exporter.rs` | Use font, shadow, dimmed color, background toggle in ASS generation | | `src/lib/components/CaptionSettingsPanel.svelte` | Add font dropdown, shadow controls, dimmed color controls, background toggle | | `src/lib/components/VideoPlayer.svelte` | Use dimmed color settings + font in preview CSS | | `src-tauri/src/commands/media_analysis.rs` (or new file) | Add `list_system_fonts` command | | `src-tauri/src/lib.rs` | Register new command | | `src/lib/bindings/mediaAnalysis.ts` (or new binding) | TS binding for `list_system_fonts` |