96 lines
5.1 KiB
Markdown
96 lines
5.1 KiB
Markdown
|
|
# 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` |
|