Files
gui-video-clipper/docs/superpowers/specs/2026-09-22-extended-caption-styling-design.md
cottongin 8ad2f1c800 chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00

5.1 KiB

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