/**
 * SubMiner Example Configuration File
 *
 * This file is auto-generated from src/config/definitions.ts.
 * Copy to %APPDATA%/SubMiner/config.jsonc on Windows, or $XDG_CONFIG_HOME/SubMiner/config.jsonc (or ~/.config/SubMiner/config.jsonc) on Linux/macOS.
 */
{

  // ==========================================
  // Dictionary Backend
  // Select the dictionary lookup backend: yomitan or hachidori.
  // Restart SubMiner after changing the backend. Each backend keeps separate settings and dictionaries.
  // ==========================================
  "dictionaryBackend": "yomitan", // Dictionary lookup backend. Restart SubMiner after changing this setting. Values: yomitan | hachidori

  // ==========================================
  // Subtitle Selection
  // Select primary and secondary mpv subtitle tracks from the overlay.
  // Hot-reload: enabling or disabling updates the session shortcut immediately.
  // ==========================================
  "subtitleSelection": {
    "enabled": false // Use the SubMiner modal to select primary and secondary subtitle tracks. When enabled, its shortcut overrides mpv subtitle selection. Values: true | false
  }, // Select primary and secondary mpv subtitle tracks from the overlay.

  // ==========================================
  // Japanese Subtitle Generation
  // Generate timed Japanese subtitles from local audio using whisper.cpp.
  // Configure an existing GGML model path or explicitly download a SubMiner-managed model.
  // Hot-reload: settings apply to the next generation or model download.
  // ==========================================
  "subtitleGeneration": {
    "whisperPath": "", // Optional path override for whisper.cpp. Leave empty to find whisper-cli on PATH.
    "modelPath": "", // Path to an existing multilingual whisper.cpp GGML model. Leave empty to use a SubMiner-managed model. A configured path always takes precedence.
    "managedModel": "small", // Multilingual whisper.cpp model to use when modelPath is empty. Download it explicitly from the generation modal or launcher. Values: tiny | tiny-q5_1 | tiny-q8_0 | base | base-q5_1 | base-q8_0 | small | small-q5_1 | small-q8_0 | medium | medium-q5_0 | medium-q8_0 | large-v1 | large-v2 | large-v2-q5_0 | large-v2-q8_0 | large-v3 | large-v3-q5_0 | large-v3-turbo | large-v3-turbo-q5_0 | large-v3-turbo-q8_0
    "threads": 4, // Positive integer CPU thread count for whisper.cpp Japanese transcription.
    "ffmpegPath": "", // Optional FFmpeg path override for audio extraction. Leave empty to find ffmpeg on PATH.
    "ffprobePath": "", // Optional FFprobe path override for audio tracks and timing. Leave empty to find ffprobe on PATH.
    "vadModelPath": "", // Path to a whisper.cpp Silero VAD model. Enables dialogue-focused generation while retaining uncertain audible sections, which may include songs. Leave empty to transcribe the full audio.
    "vadPath": "" // Optional speech detector executable override. With vadModelPath configured, leave empty to find whisper-vad-speech-segments or vad-speech-segments on PATH.
  }, // Generate timed Japanese subtitles from local audio using whisper.cpp.

  // ==========================================
  // Visible Overlay Auto-Start
  // Show the visible subtitle overlay automatically after managed mpv playback starts SubMiner.
  // SubMiner can still auto-start in the background when this is false.
  // ==========================================
  "auto_start_overlay": true, // Show the visible subtitle overlay automatically when the bundled mpv plugin starts SubMiner. Values: true | false

  // ==========================================
  // Texthooker Server
  // Configure texthooker startup launch and browser opening behavior.
  // ==========================================
  "texthooker": {
    "launchAtStartup": false, // Launch texthooker server automatically when SubMiner starts. Values: true | false
    "openBrowser": false // Open the texthooker page in the default browser when the server starts. Values: true | false
  }, // Configure texthooker startup launch and browser opening behavior.

  // ==========================================
  // WebSocket Server
  // Built-in WebSocket server broadcasts subtitle text to connected clients.
  // Auto mode disables built-in server if mpv_websocket is detected.
  // ==========================================
  "websocket": {
    "enabled": false, // Built-in subtitle websocket server mode. Values: auto | true | false
    "port": 6677 // Built-in subtitle websocket server port.
  }, // Built-in WebSocket server broadcasts subtitle text to connected clients.

  // ==========================================
  // Annotation WebSocket
  // Dedicated annotated subtitle websocket for bundled texthooker and token-aware clients.
  // Independent from websocket.auto and defaults to port 6678.
  // ==========================================
  "annotationWebsocket": {
    "enabled": false, // Annotated subtitle websocket server enabled state. Values: true | false
    "port": 6678 // Annotated subtitle websocket server port.
  }, // Dedicated annotated subtitle websocket for bundled texthooker and token-aware clients.

  // ==========================================
  // Logging
  // Controls logging verbosity.
  // Set to debug for full runtime diagnostics.
  // Hot-reload: logging.level and logging.files apply live while SubMiner is running.
  // ==========================================
  "logging": {
    "level": "warn", // Minimum log level for runtime logging. Values: debug | info | warn | error
    "rotation": 7, // Number of days of app, launcher, and mpv logs to retain.
    "files": {
      "app": true, // Write SubMiner app runtime logs. Values: true | false
      "launcher": true, // Write launcher command logs. Values: true | false
      "mpv": false // Write mpv player logs. Enable temporarily when debugging mpv/plugin startup. Values: true | false
    } // Files setting.
  }, // Controls logging verbosity.

  // ==========================================
  // Controller Support
  // Gamepad support for the visible overlay while keyboard-only mode is active.
  // Use Alt+C to pick a preferred controller and remap actions inline with learn mode.
  // Trigger input mode can be auto, digital-only, or analog-thresholded depending on the controller.
  // Override controller.buttonIndices when your pad reports non-standard raw button numbers.
  // ==========================================
  "controller": {
    "enabled": false, // Enable overlay controller support through the Chrome Gamepad API. Values: true | false
    "preferredGamepadId": "", // Preferred controller id saved from the controller config modal.
    "preferredGamepadLabel": "", // Preferred controller display label saved for diagnostics.
    "smoothScroll": true, // Use smooth scrolling for controller-driven popup scroll input. Values: true | false
    "scrollPixelsPerSecond": 900, // Base popup scroll speed for controller stick input.
    "horizontalJumpPixels": 160, // Popup page-jump distance for controller jump input.
    "stickDeadzone": 0.2, // Deadzone applied to controller stick axes.
    "triggerInputMode": "auto", // How controller triggers are interpreted: auto, pressed-only, or thresholded analog. Values: auto | digital | analog
    "triggerDeadzone": 0.5, // Minimum analog trigger value required when trigger input uses auto or analog mode.
    "repeatDelayMs": 320, // Delay before repeating held controller actions.
    "repeatIntervalMs": 120, // Repeat interval for held controller actions.
    "buttonIndices": {
      "select": 6, // Raw button index used for the controller select/minus/back button.
      "buttonSouth": 0, // Raw button index used for controller south/A button input.
      "buttonEast": 1, // Raw button index used for controller east/B button input.
      "buttonWest": 2, // Raw button index used for controller west/X button input.
      "buttonNorth": 3, // Raw button index used for controller north/Y button input.
      "leftShoulder": 4, // Raw button index used for controller left shoulder input.
      "rightShoulder": 5, // Raw button index used for controller right shoulder input.
      "leftStickPress": 9, // Raw button index used for controller L3 input.
      "rightStickPress": 10, // Raw button index used for controller R3 input.
      "leftTrigger": 6, // Raw button index used for controller L2 input.
      "rightTrigger": 7 // Raw button index used for controller R2 input.
    }, // Semantic button-name reference mapping used for debug output. Updating it does not rewrite existing raw binding descriptors.
    "bindings": {
      "toggleLookup": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 0 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for toggling lookup. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "closeLookup": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 1 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for closing lookup. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "toggleKeyboardOnlyMode": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 3 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for toggling keyboard-only mode. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "mineCard": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 2 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for mining the active card. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "quitMpv": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 6 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for quitting mpv. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "previousAudio": {
        "kind": "none" // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
      }, // Controller binding descriptor for previous Yomitan audio. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "nextAudio": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 5 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for next Yomitan audio. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "playCurrentAudio": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 4 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for playing the current Yomitan audio. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "toggleMpvPause": {
        "kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
        "buttonIndex": 9 // Raw button index captured for this discrete controller action.
      }, // Controller binding descriptor for toggling mpv play/pause. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
      "leftStickHorizontal": {
        "kind": "axis", // Analog binding input source kind. Values: none | axis
        "axisIndex": 0, // Raw axis index captured for this analog controller action.
        "dpadFallback": "horizontal" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
      }, // Axis binding descriptor used for left/right token selection. Use Alt+C learn mode or set a raw axis descriptor manually.
      "leftStickVertical": {
        "kind": "axis", // Analog binding input source kind. Values: none | axis
        "axisIndex": 1, // Raw axis index captured for this analog controller action.
        "dpadFallback": "vertical" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
      }, // Axis binding descriptor used for primary popup scrolling. Use Alt+C learn mode or set a raw axis descriptor manually.
      "rightStickHorizontal": {
        "kind": "axis", // Analog binding input source kind. Values: none | axis
        "axisIndex": 3, // Raw axis index captured for this analog controller action.
        "dpadFallback": "none" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
      }, // Axis binding descriptor reserved for alternate right-stick mappings. Use Alt+C learn mode or set a raw axis descriptor manually.
      "rightStickVertical": {
        "kind": "axis", // Analog binding input source kind. Values: none | axis
        "axisIndex": 4, // Raw axis index captured for this analog controller action.
        "dpadFallback": "none" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
      } // Axis binding descriptor used for popup page jumps. Use Alt+C learn mode or set a raw axis descriptor manually.
    }, // Raw controller binding descriptors saved by Alt+C learn mode. For discrete axis bindings, kind "axis" requires axisIndex and direction.
    "profiles": {} // Per-controller binding and button-index overrides keyed by the controller id reported by the Gamepad API.
  }, // Gamepad support for the visible overlay while keyboard-only mode is active.

  // ==========================================
  // Startup Warmups
  // Background warmup controls for MeCab, Yomitan, dictionaries, and Jellyfin session.
  // Disable individual warmups to defer load until first real usage.
  // lowPowerMode defers all warmups except Yomitan extension.
  // ==========================================
  "startupWarmups": {
    "lowPowerMode": false, // Defer startup warmups except Yomitan extension. Values: true | false
    "mecab": true, // Warm up MeCab tokenizer at startup. Values: true | false
    "yomitanExtension": true, // Warm up Yomitan extension at startup. Values: true | false
    "subtitleDictionaries": true, // Warm up subtitle dictionaries at startup. Values: true | false
    "jellyfinRemoteSession": false // Warm up Jellyfin remote session at startup. Values: true | false
  }, // Background warmup controls for MeCab, Yomitan, dictionaries, and Jellyfin session.

  // ==========================================
  // Updates
  // Automatic update check behavior.
  // Manual checks from the tray or launcher are always allowed.
  // ==========================================
  "updates": {
    "enabled": true, // Run automatic update checks in the background. Values: true | false
    "checkIntervalHours": 24, // Minimum hours between automatic update checks.
    "notificationType": "overlay", // How SubMiner announces available updates. overlay shows notifications on the overlay, system uses OS notifications, both uses overlay and system. osd and osd-system are legacy config-file-only values. Values: overlay | system | both | none | osd | osd-system
    "channel": "stable" // Release channel used for update checks. Values: stable | prerelease
  }, // Automatic update check behavior.

  // ==========================================
  // Notifications
  // Overlay notification display behavior.
  // Hot-reload: position changes apply to the next overlay notification.
  // ==========================================
  "notifications": {
    "overlayPosition": "top-right" // Position for in-overlay notification cards. Values: top-left | top | top-right
  }, // Overlay notification display behavior.

  // ==========================================
  // Keyboard Shortcuts
  // Overlay keyboard shortcuts. Set a shortcut to null to disable.
  // Hot-reload: shortcut changes apply live and update the session help modal on reopen.
  // ==========================================
  "shortcuts": {
    "toggleVisibleOverlayGlobal": "Alt+Shift+O", // Global accelerator that toggles overlay visibility from anywhere on the system. Use null to disable.
    "copySubtitle": "CommandOrControl+C", // Accelerator that copies the current subtitle line to the clipboard.
    "copySubtitleMultiple": "CommandOrControl+Shift+C", // Accelerator that copies consecutive subtitle lines while the multi-copy window stays open.
    "updateLastCardFromClipboard": "CommandOrControl+V", // Accelerator that updates the last mined Anki card using the current clipboard contents.
    "triggerFieldGrouping": "CommandOrControl+G", // Accelerator that triggers Kiku field grouping on duplicate cards.
    "triggerSubsync": "Ctrl+Alt+S", // Accelerator that triggers subsync against the active subtitle file.
    "mineSentence": "CommandOrControl+S", // Accelerator that mines the current sentence as a new Anki card.
    "mineSentenceMultiple": "CommandOrControl+Shift+S", // Accelerator that mines consecutive sentences while the multi-mine window stays open.
    "multiCopyTimeoutMs": 3000, // Timeout for multi-copy/mine modes.
    "toggleSecondarySub": "CommandOrControl+Shift+V", // Accelerator that toggles the secondary subtitle bar visibility.
    "markAudioCard": "CommandOrControl+Shift+A", // Accelerator that marks the last mined card as an audio card.
    "openCharacterDictionaryManager": "CommandOrControl+D", // Accelerator that opens the character dictionary manager modal.
    "openRuntimeOptions": "CommandOrControl+Shift+O", // Accelerator that opens the runtime options modal.
    "openJimaku": "Ctrl+Shift+J", // Accelerator that opens the Jimaku subtitle search modal.
    "openTsukihime": "Ctrl+Shift+T", // Accelerator that opens the TsukiHime subtitle search modal (configured secondary/Japanese primary tabs).
    "openSubtitleSelection": "g-s", // Open subtitle selection when enabled. Use g-s to press g then s. Set null to unbind.
    "openSubtitleGeneration": "Ctrl+Shift+G", // Accelerator that opens the standalone Japanese subtitle generation modal.
    "openSessionHelp": "CommandOrControl+Slash", // Accelerator that opens the session help / keybinding cheatsheet.
    "openControllerSelect": "Alt+C", // Accelerator that opens the controller selection and learn-mode modal.
    "openControllerDebug": "Alt+Shift+C", // Accelerator that opens the controller debug modal with live axis/button readouts.
    "toggleSubtitleSidebar": "Backslash", // Accelerator that toggles the subtitle sidebar visibility.
    "toggleNotificationHistory": "CommandOrControl+N", // Accelerator that toggles the overlay notification history panel.
    "appendClipboardVideoToQueue": "CommandOrControl+A" // Accelerator that appends a video path from the clipboard to the mpv playlist.
  }, // Overlay keyboard shortcuts. Set a shortcut to null to disable.

  // ==========================================
  // Keybindings (MPV Commands)
  // Default and custom keybindings that are merged with built-in defaults.
  // Set command to null to disable a default keybinding.
  // Hot-reload: keybinding changes apply live and update the session help modal on reopen.
  // ==========================================
  "keybindings": [
    {
      "key": "Space", // Key setting.
      "command": [
        "cycle",
        "pause"
      ] // Command setting.
    },
    {
      "key": "KeyF", // Key setting.
      "command": [
        "cycle",
        "fullscreen"
      ] // Command setting.
    },
    {
      "key": "KeyJ", // Key setting.
      "command": [
        "cycle",
        "sid"
      ] // Command setting.
    },
    {
      "key": "Shift+KeyJ", // Key setting.
      "command": [
        "cycle",
        "secondary-sid"
      ] // Command setting.
    },
    {
      "key": "ArrowRight", // Key setting.
      "command": [
        "seek",
        5
      ] // Command setting.
    },
    {
      "key": "ArrowLeft", // Key setting.
      "command": [
        "seek",
        -5
      ] // Command setting.
    },
    {
      "key": "ArrowUp", // Key setting.
      "command": [
        "seek",
        60
      ] // Command setting.
    },
    {
      "key": "ArrowDown", // Key setting.
      "command": [
        "seek",
        -60
      ] // Command setting.
    },
    {
      "key": "Shift+KeyH", // Key setting.
      "command": [
        "sub-seek",
        -1
      ] // Command setting.
    },
    {
      "key": "Shift+KeyL", // Key setting.
      "command": [
        "sub-seek",
        1
      ] // Command setting.
    },
    {
      "key": "Ctrl+Shift+ArrowLeft", // Key setting.
      "command": [
        "sub-step",
        -1
      ] // Command setting.
    },
    {
      "key": "Ctrl+Shift+ArrowRight", // Key setting.
      "command": [
        "sub-step",
        1
      ] // Command setting.
    },
    {
      "key": "KeyZ", // Key setting.
      "command": [
        "add",
        "sub-delay",
        -0.1
      ] // Command setting.
    },
    {
      "key": "Shift+KeyZ", // Key setting.
      "command": [
        "add",
        "sub-delay",
        0.1
      ] // Command setting.
    },
    {
      "key": "KeyX", // Key setting.
      "command": [
        "add",
        "sub-delay",
        0.1
      ] // Command setting.
    },
    {
      "key": "Ctrl+Alt+KeyC", // Key setting.
      "command": [
        "__youtube-picker-open"
      ] // Command setting.
    },
    {
      "key": "Ctrl+Alt+KeyP", // Key setting.
      "command": [
        "__playlist-browser-open"
      ] // Command setting.
    },
    {
      "key": "Ctrl+Shift+KeyH", // Key setting.
      "command": [
        "__replay-subtitle"
      ] // Command setting.
    },
    {
      "key": "Ctrl+Shift+KeyL", // Key setting.
      "command": [
        "__play-next-subtitle"
      ] // Command setting.
    },
    {
      "key": "KeyQ", // Key setting.
      "command": [
        "quit"
      ] // Command setting.
    },
    {
      "key": "Ctrl+KeyW", // Key setting.
      "command": [
        "quit"
      ] // Command setting.
    }
  ], // Default and custom keybindings that are merged with built-in defaults.

  // ==========================================
  // Secondary Subtitles
  // Dual subtitle track options.
  // Used by managed subtitle loading as secondary language preferences for local and YouTube playback.
  // Hot-reload: defaultMode updates live while SubMiner is running.
  // ==========================================
  "secondarySub": {
    "secondarySubLanguages": [], // Language code priority list used to auto-select a secondary subtitle track when available.
    "autoLoadSecondarySub": false, // Automatically load a matching secondary subtitle when the primary subtitle loads. Values: true | false
    "defaultMode": "hover" // Default visibility mode for the secondary subtitle bar. Values: hidden | visible | hover
  }, // Dual subtitle track options.

  // ==========================================
  // Subtitle Sync
  // Subsync engine and executable paths.
  // Hot-reload: subsync changes apply to the next subtitle sync run.
  // ==========================================
  "subsync": {
    "alass_path": "", // Optional absolute path to the alass binary used by subsync. Leave empty to auto-discover from PATH.
    "ffsubsync_path": "", // Optional absolute path to the ffsubsync binary used by subsync. Leave empty to auto-discover from PATH.
    "ffmpeg_path": "", // Optional absolute path to the ffmpeg binary used by subsync. Leave empty to auto-discover from PATH.
    "replace": true // Replace the active subtitle file when sync completes. Values: true | false
  }, // Subsync engine and executable paths.

  // ==========================================
  // Subtitle Position
  // Initial vertical subtitle position from the bottom.
  // ==========================================
  "subtitlePosition": {
    "yPercent": 10 // Vertical position of the subtitle overlay expressed as a percentage from the bottom of the screen.
  }, // Initial vertical subtitle position from the bottom.

  // ==========================================
  // Subtitle Appearance
  // Primary and secondary subtitle styling.
  // Hot-reload: subtitle style changes apply live without restarting SubMiner.
  // ==========================================
  "subtitleStyle": {
    "primaryDefaultMode": "visible", // Default primary subtitle bar visibility mode. hidden hides it, visible shows it, hover reveals it on hover. Values: hidden | visible | hover
    "css": {
      "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting.
      "color": "#cad3f5", // Color setting.
      "background-color": "transparent", // Background color setting.
      "font-size": "35px", // Font size setting.
      "font-weight": "600", // Font weight setting.
      "font-style": "normal", // Font style setting.
      "line-height": "1.35", // Line height setting.
      "letter-spacing": "-0.01em", // Letter spacing setting.
      "word-spacing": "0", // Word spacing setting.
      "font-kerning": "normal", // Font kerning setting.
      "text-rendering": "geometricPrecision", // Text rendering setting.
      "text-shadow": "-1px -1px 2px rgba(0,0,0,0.95), 1px -1px 2px rgba(0,0,0,0.95), -1px 1px 2px rgba(0,0,0,0.95), 1px 1px 2px rgba(0,0,0,0.95), 0 0 8px rgba(0,0,0,0.5)", // Text shadow setting.
      "backdrop-filter": "blur(6px)", // Backdrop filter setting.
      "--subtitle-hover-token-color": "#f4dbd6", // Subtitle hover token color setting.
      "--subtitle-hover-token-background-color": "transparent" // Subtitle hover token background color setting.
    }, // CSS declaration object applied to primary subtitles after normal subtitle style defaults.
    "enableJlpt": false, // Enable JLPT vocabulary level underlines. When disabled, JLPT tagging lookup and underlines are skipped. Values: true | false
    "preserveLineBreaks": false, // Preserve line breaks in visible overlay subtitle rendering. When false, line breaks are flattened to spaces for a single-line flow. Values: true | false
    "autoPauseVideoOnHover": true, // Automatically pause mpv playback while hovering subtitle text, then resume on leave. Values: true | false
    "autoPauseVideoOnYomitanPopup": true, // Automatically pause mpv playback while Yomitan popup is open, then resume when popup closes. Values: true | false
    "primaryVisibleOnYomitanPopup": true, // Keep the primary subtitle bar visible while a Yomitan popup is open when primary subtitles are in hover mode. Values: true | false
    "nameMatchEnabled": false, // Enable character dictionary sync and subtitle token coloring for character-name matches. Values: true | false
    "nameMatchImagesEnabled": false, // Show small character portraits beside subtitle tokens matched from the SubMiner character dictionary. Values: true | false
    "nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
    "nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight.
    "knownWordColor": "#a6da95", // Color used for known-word subtitle highlights.
    "knownWordMaturityColors": {
      "new": "#ee99a0", // Color for known words whose Anki cards are new (never reviewed), when maturity highlighting is enabled.
      "learning": "#b7bdf8", // Color for known words whose Anki cards are in (re)learning, when maturity highlighting is enabled.
      "young": "#91d7e3", // Color for known words whose Anki cards are in review below the mature threshold, when maturity highlighting is enabled.
      "mature": "#a6da95" // Color for known words whose Anki cards are at or above the mature interval threshold, when maturity highlighting is enabled.
    }, // Known word maturity colors setting.
    "jlptColors": {
      "N1": "#ed8796", // N1 setting.
      "N2": "#f5a97f", // N2 setting.
      "N3": "#f9e2af", // N3 setting.
      "N4": "#8bd5ca", // N4 setting.
      "N5": "#8aadf4" // N5 setting.
    }, // Jlpt colors setting.
    "frequencyDictionary": {
      "enabled": false, // Enable frequency-dictionary-based highlighting based on token rank. Values: true | false
      "sourcePath": "", // Optional absolute path to a frequency dictionary directory. If empty, built-in discovery search paths are used.
      "topX": 10000, // Only color tokens with frequency rank <= topX (default: 10000).
      "mode": "single", // single: use one color for all matching tokens. banded: use color ramp by frequency band. Values: single | banded
      "matchMode": "headword", // headword: frequency lookup uses dictionary form. surface: lookup uses subtitle-visible token text. Values: headword | surface
      "singleColor": "#f5a97f", // Color used when frequencyDictionary.mode is `single`.
      "bandedColors": [
        "#ed8796",
        "#f5a97f",
        "#f9e2af",
        "#8bd5ca",
        "#8aadf4"
      ] // Five colors used for rank bands when mode is `banded` (from most common to least within topX).
    }, // Frequency dictionary setting.
    "secondary": {
      "css": {
        "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting.
        "color": "#cad3f5", // Color setting.
        "background-color": "transparent", // Background color setting.
        "font-size": "24px", // Font size setting.
        "font-weight": "600", // Font weight setting.
        "font-style": "normal", // Font style setting.
        "line-height": "1.35", // Line height setting.
        "letter-spacing": "-0.01em", // Letter spacing setting.
        "word-spacing": "0", // Word spacing setting.
        "font-kerning": "normal", // Font kerning setting.
        "text-rendering": "geometricPrecision", // Text rendering setting.
        "text-shadow": "-1px -1px 2px rgba(0,0,0,0.95), 1px -1px 2px rgba(0,0,0,0.95), -1px 1px 2px rgba(0,0,0,0.95), 1px 1px 2px rgba(0,0,0,0.95), 0 0 8px rgba(0,0,0,0.5)", // Text shadow setting.
        "backdrop-filter": "blur(6px)" // Backdrop filter setting.
      } // CSS declaration object applied to secondary subtitles after normal subtitle style defaults.
    } // Secondary setting.
  }, // Primary and secondary subtitle styling.

  // ==========================================
  // Subtitle Sidebar
  // Parsed-subtitle sidebar cue list styling, behavior, and toggle key.
  // Hot-reload: subtitle sidebar changes apply live without restarting SubMiner.
  // ==========================================
  "subtitleSidebar": {
    "enabled": true, // Enable the subtitle sidebar feature for parsed subtitle sources. Values: true | false
    "autoOpen": false, // Automatically open the subtitle sidebar once during overlay startup. Values: true | false
    "layout": "overlay", // Render the subtitle sidebar as a floating overlay or reserve space inside mpv. Values: overlay | embedded
    "toggleKey": "Backslash", // KeyboardEvent.code used to toggle the subtitle sidebar open and closed.
    "pauseVideoOnHover": true, // Pause mpv while hovering the subtitle sidebar, then resume on leave. Values: true | false
    "autoScroll": true, // Auto-scroll the active subtitle cue into view while playback advances. Values: true | false
    "css": {
      "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting.
      "color": "#cad3f5", // Color setting.
      "background-color": "rgba(73, 77, 100, 0.9)", // Background color setting.
      "font-size": "16px", // Font size setting.
      "opacity": "0.95", // Opacity setting.
      "--subtitle-sidebar-max-width": "420px", // Subtitle sidebar max width setting.
      "--subtitle-sidebar-timestamp-color": "#a5adcb", // Subtitle sidebar timestamp color setting.
      "--subtitle-sidebar-active-line-color": "#f5bde6", // Subtitle sidebar active line color setting.
      "--subtitle-sidebar-active-background-color": "rgba(138, 173, 244, 0.22)", // Subtitle sidebar active background color setting.
      "--subtitle-sidebar-hover-background-color": "rgba(54, 58, 79, 0.84)" // Subtitle sidebar hover background color setting.
    } // CSS declaration object applied to the subtitle sidebar. Includes color, background-color, and all font properties.
  }, // Parsed-subtitle sidebar cue list styling, behavior, and toggle key.

  // ==========================================
  // Shared AI Provider
  // Canonical OpenAI-compatible provider transport settings shared by Anki and YouTube subtitle fixing.
  // ==========================================
  "ai": {
    "enabled": false, // Enable shared OpenAI-compatible AI provider features. Values: true | false
    "apiKey": "", // Static API key for the shared OpenAI-compatible AI provider.
    "apiKeyCommand": "", // Shell command used to resolve the shared AI provider API key.
    "model": "openai/gpt-4o-mini", // Default model identifier requested from the shared AI provider.
    "baseUrl": "https://openrouter.ai/api", // Base URL for the shared OpenAI-compatible AI provider.
    "systemPrompt": "You are a translation engine. Return only the translated text with no explanations.", // Default system prompt sent with shared AI provider requests.
    "requestTimeoutMs": 15000 // Timeout in milliseconds for shared AI provider requests.
  }, // Canonical OpenAI-compatible provider transport settings shared by Anki and YouTube subtitle fixing.

  // ==========================================
  // AnkiConnect Integration
  // Automatic Anki updates and media generation options.
  // Hot-reload: ankiConnect.ai.enabled, media.normalizeAudio/mirrorMpvVolume/reviewTiming, knownWords, nPlusOne, fields.word/audio/wordAudio/image/sentence/miscInfo, behavior.autoUpdateNewCards, isLapis.sentenceCardModel, isKiku.fieldGrouping, isSenren.fieldGrouping, and lapisKiku.wordCardKind update live while SubMiner is running.
  // Shared AI provider transport settings are read from top-level ai and typically require restart.
  // Most other AnkiConnect settings still require restart.
  // ==========================================
  "ankiConnect": {
    "enabled": true, // Enable AnkiConnect integration. Values: true | false
    "url": "http://127.0.0.1:8765", // Base URL of the AnkiConnect HTTP server.
    "pollingRate": 3000, // Polling interval in milliseconds.
    "proxy": {
      "enabled": true, // Enable local AnkiConnect-compatible proxy for push-based auto-enrichment. Values: true | false
      "host": "127.0.0.1", // Bind host for local AnkiConnect proxy.
      "port": 8766, // Bind port for local AnkiConnect proxy.
      "upstreamUrl": "http://127.0.0.1:8765" // Upstream AnkiConnect URL proxied by local AnkiConnect proxy.
    }, // Proxy setting.
    "tags": [
      "SubMiner"
    ], // Tags to add to cards mined or updated by SubMiner. Provide an empty array to disable automatic tagging.
    "deck": "", // Restrict duplicate detection and card enrichment to this Anki deck. Leave empty to use the Yomitan mining deck when available.
    "fields": {
      "word": "Expression", // Card field for the mined word or expression text.
      "audio": "ExpressionAudio", // Card field that receives generated sentence audio.
      "wordAudio": "ExpressionAudio", // Existing word-audio field read to time the frozen first frame of animated images. This mapping is only used for synchronization.
      "image": "Picture", // Card field that receives the captured screenshot or animated image.
      "sentence": "Sentence", // Card field that receives the source sentence text.
      "miscInfo": "MiscInfo", // Card field that receives the miscellaneous info pattern (see ankiConnect.metadata.pattern).
      "translation": "SelectionText" // Card field that receives the current selection or translated text.
    }, // Fields setting.
    "ai": {
      "enabled": false, // Enable AI provider usage for Anki translation/enrichment flows. Values: true | false
      "model": "", // Optional model override for Anki AI translation/enrichment flows.
      "systemPrompt": "" // Optional system prompt override for Anki AI translation/enrichment flows.
    }, // Ai setting.
    "media": {
      "generateAudio": true, // Generate sentence audio for mined cards. Values: true | false
      "generateImage": true, // Generate screenshot or animated image for mined cards. Values: true | false
      "imageType": "static", // Image capture type: "static" for a single still frame, "avif" for an animated AVIF. Values: static | avif
      "imageFormat": "jpg", // Encoding format used when imageType is "static". Values: jpg | png | webp
      "imageQuality": 92, // Quality (0-100) used for lossy static image encoders.
      "imageMaxWidth": 0, // Maximum width for static images, in pixels. Set to 0 to preserve the source resolution.
      "imageMaxHeight": 0, // Maximum height for static images, in pixels. Set to 0 to preserve the source resolution.
      "animatedFps": 10, // Target frame rate for animated AVIF captures.
      "animatedMaxWidth": 640, // Maximum width applied to animated AVIF captures.
      "animatedMaxHeight": 0, // Maximum height for animated AVIF captures, in pixels. Set to 0 to preserve aspect ratio.
      "animatedCrf": 35, // Animated AVIF CRF quality target. Lower values produce larger, higher-quality files.
      "syncAnimatedImageToWordAudio": true, // For animated AVIF images, prepend a frozen first frame matching the existing word-audio duration so motion starts with sentence audio. Values: true | false
      "normalizeAudio": true, // Normalize generated sentence audio loudness during media extraction. Changes apply live. Values: true | false
      "mirrorMpvVolume": true, // Apply mpv's current software volume curve to generated sentence audio. Changes apply live. Values: true | false
      "reviewTiming": false, // Review and preview subtitle media timing before SubMiner creates or enriches a mined card. Values: true | false
      "audioPadding": 0, // Seconds of padding appended to both ends of generated sentence audio and animated AVIF clips.
      "fallbackDuration": 3, // Fallback clip duration in seconds when subtitle timing data is unavailable.
      "maxMediaDuration": 30 // Maximum allowed media clip duration in seconds. 0 disables the cap.
    }, // Media setting.
    "knownWords": {
      "highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
      "maturityEnabled": false, // Color known-word highlights by Anki card maturity (new, learning, young, mature) instead of a single color. Requires known-word highlighting. Values: true | false
      "matureThresholdDays": 21, // Card interval in days at which a known word counts as mature (Anki convention: 21).
      "refreshMinutes": 1440, // Minutes between known-word cache refreshes.
      "addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false
      "matchMode": "headword", // Known-word matching strategy for subtitle annotations. Cache matches always receive known-word highlighting even when POS filters suppress other annotation types. Values: headword | surface
      "decks": {} // Decks and expression/word fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word"] }. Reading fields (Reading, Word Reading, ExpressionReading) are always probed so cached words match only in the reading their note teaches; words from notes without readings match in any reading.
    }, // Known words setting.
    "behavior": {
      "overwriteAudio": true, // When updating an existing card, overwrite the audio field instead of skipping it. Values: true | false
      "overwriteImage": true, // When updating an existing card, overwrite the image field instead of skipping it. Values: true | false
      "mediaInsertMode": "append", // Whether new media is appended after or prepended before existing field contents on update. Values: append | prepend
      "highlightWord": true, // Bold the mined word inside the sentence field on the saved Anki card. Values: true | false
      "notificationType": "overlay", // Notification surface used to announce mining and update outcomes. overlay shows notifications on the overlay, system uses OS notifications, both uses overlay and system. osd and osd-system are legacy config-file-only values. Values: overlay | system | both | none | osd | osd-system
      "autoUpdateNewCards": true // Automatically update newly added cards. Values: true | false
    }, // Behavior setting.
    "nPlusOne": {
      "enabled": false, // Enable N+1 subtitle highlighting (highlights the one unknown word in a sentence). Requires known-word cache data. Values: true | false
      "minSentenceWords": 3 // Minimum sentence word count required for N+1 targeting (default: 3).
    }, // N plus one setting.
    "metadata": {
      "pattern": "[SubMiner] %f (%t)" // Template used to render the miscInfo field. Placeholders include %f (filename) and %t (timestamp).
    }, // Metadata setting.
    "isLapis": {
      "enabled": false, // Enable Lapis-specific mining behaviors and sentence card model targeting. Values: true | false
      "sentenceCardModel": "Lapis" // Note type name used by Lapis sentence cards.
    }, // Is lapis setting.
    "isKiku": {
      "enabled": false, // Enable Kiku-specific mining behaviors (duplicate handling, field grouping). Values: true | false
      "fieldGrouping": "disabled", // Kiku duplicate-card field grouping mode. Values: auto | manual | disabled
      "deleteDuplicateInAuto": true // When Kiku field grouping is "auto", delete the duplicate source card after grouping completes. Values: true | false
    }, // Is kiku setting.
    "isSenren": {
      "enabled": false, // Enable Senren-specific duplicate handling (scene-switching field grouping, including miscInfo grouping). Mutually exclusive with isKiku.enabled. Values: true | false
      "fieldGrouping": "auto", // Senren duplicate-card field grouping mode (scene switching). Values: auto | manual | disabled
      "deleteDuplicateInAuto": true // When Senren field grouping is "auto", delete the duplicate source card after grouping completes. Values: true | false
    }, // Is senren setting.
    "lapisKiku": {
      "wordCardKind": "word-and-sentence" // Card-type flag SubMiner marks on Kiku/Lapis word cards. Only one flag is set at a time; the others are cleared. Requires isKiku.enabled or isLapis.enabled. Values: word-and-sentence | click | sentence | audio | none
    } // Lapis kiku setting.
  }, // Automatic Anki updates and media generation options.

  // ==========================================
  // Jimaku
  // Jimaku API configuration and defaults.
  // Hot-reload: Jimaku changes apply to the next Jimaku request.
  // ==========================================
  "jimaku": {
    "apiBaseUrl": "https://jimaku.cc", // Base URL of the Jimaku subtitle search API.
    "apiKey": "", // Jimaku API key. Optional but recommended for higher rate limits. Get one for free at https://jimaku.cc.
    "apiKeyCommand": "", // Shell command that prints the Jimaku API key to stdout. Used instead of apiKey to avoid storing the key in plain text.
    "languagePreference": "ja", // Preferred language used in Jimaku search. Values: ja | en | none
    "maxEntryResults": 10 // Maximum Jimaku search results returned.
  }, // Jimaku API configuration and defaults.

  // ==========================================
  // TsukiHime
  // TsukiHime subtitle search configuration for Japanese primary and configured secondary subtitles. No API key required.
  // Hot-reload: TsukiHime changes apply to the next TsukiHime request.
  // ==========================================
  "tsukihime": {
    "apiBaseUrl": "https://api.tsukihime.org/v1", // Base URL of the TsukiHime API (Animetosho successor). No API key required.
    "maxSearchResults": 10 // Maximum TsukiHime search results returned.
  }, // TsukiHime subtitle search configuration for Japanese primary and configured secondary subtitles. No API key required.

  // ==========================================
  // TMDB
  // TMDB (The Movie Database) metadata for live-action dramas and movies in the stats Library: posters, synopses, and grouping by show.
  // Hot-reload: TMDB changes apply to the next TMDB request.
  // ==========================================
  "tmdb": {
    "apiKey": "", // Your own TMDB API key or read access token for live-action posters and synopses in the stats Library. Release builds bundle a project key, so set this only to use your own quota or when running from source (free under Settings > API on themoviedb.org).
    "apiKeyCommand": "" // Shell command that prints the TMDB API key to stdout. Used instead of apiKey to avoid storing the key in plain text.
  }, // TMDB (The Movie Database) metadata for live-action dramas and movies in the stats Library: posters, synopses, and grouping by show.

  // ==========================================
  // YouTube Playback Settings
  // Defaults for managed subtitle language preferences and YouTube subtitle loading.
  // Hot-reload: primarySubLanguages and subtitleSource apply to the next YouTube subtitle load.
  // ==========================================
  "youtube": {
    "primarySubLanguages": [
      "ja",
      "jpn"
    ], // Comma-separated primary subtitle language priority for managed subtitle auto-selection.
    "subtitleSource": "youtube", // Where primary YouTube subtitles come from. Whisper transcribes the audio locally using the subtitleGeneration settings. Values: youtube | whisper
    "mediaCache": {
      "mode": "direct", // How YouTube card audio/images are extracted. Values: direct | background
      "maxHeight": 720 // Maximum video height downloaded for the YouTube background media cache. Set to 0 for unlimited.
    } // Media cache setting.
  }, // Defaults for managed subtitle language preferences and YouTube subtitle loading.

  // ==========================================
  // Anilist
  // Anilist API credentials and update behavior.
  // Includes optional auto-sync for a merged MRU-based character dictionary in bundled Yomitan.
  // Character dictionaries are keyed by AniList media ID (no season/franchise merge).
  // ==========================================
  "anilist": {
    "enabled": false, // Enable AniList post-watch progress updates. Values: true | false
    "accessToken": "", // Optional explicit AniList access token override; leave empty to use locally stored token from setup.
    "characterDictionary": {
      "maxLoaded": 3, // Maximum number of most-recently-used anime snapshots included in the merged Yomitan character dictionary.
      "profileScope": "all", // Yomitan profile scope for character dictionary settings updates. Values: all | active
      "collapsibleSections": {
        "description": false, // Open the Description section by default in character dictionary glossary entries. Values: true | false
        "characterInformation": false, // Open the Character Information section by default in character dictionary glossary entries. Values: true | false
        "voicedBy": false // Open the Voiced by section by default in character dictionary glossary entries. Values: true | false
      } // Collapsible sections setting.
    } // Character dictionary setting.
  }, // Anilist API credentials and update behavior.

  // ==========================================
  // Yomitan
  // Optional external Yomitan profile integration.
  // Setting yomitan.externalProfilePath switches SubMiner to read-only external-profile mode.
  // For GameSentenceMiner on Linux, the default overlay profile is usually ~/.config/gsm_overlay.
  // In external-profile mode SubMiner will not import, delete, or modify Yomitan dictionaries/settings.
  // ==========================================
  "yomitan": {
    "externalProfilePath": "" // Optional external Yomitan Electron profile path to use in read-only mode for shared dictionaries/settings. Example: ~/.config/gsm_overlay
  }, // Optional external Yomitan profile integration.

  // ==========================================
  // MPV Launcher
  // SubMiner-managed mpv launch and bundled plugin options.
  // Set mpv.socketPath to the IPC socket used by the launcher, Electron app, and bundled plugin.
  // autoStartSubMiner starts SubMiner in the background; auto_start_overlay only controls visible overlay display.
  // Set mpv.launchMode to choose normal, maximized, or fullscreen SubMiner-managed mpv playback.
  // Set mpv.profile to pass an mpv profile to managed mpv launches; leave it blank to pass none.
  // Leave mpv.executablePath blank to auto-discover mpv from SUBMINER_MPV_PATH, PATH, or (macOS) Homebrew and /Applications/mpv.app.
  // ==========================================
  "mpv": {
    "executablePath": "", // Optional absolute path to the mpv binary SubMiner starts itself (Windows launches, Jellyfin casting, timing preview). Leave empty to auto-discover from SUBMINER_MPV_PATH, PATH, and on macOS the Homebrew and /Applications/mpv.app locations.
    "launchMode": "normal", // Default window state for SubMiner-managed mpv launches. Values: normal | maximized | fullscreen
    "profile": "", // Optional mpv profile name passed to SubMiner-managed mpv launches. Leave empty to pass no profile.
    "socketPath": "/tmp/subminer-socket", // mpv IPC socket path used by SubMiner-managed playback and the bundled mpv plugin.
    "backend": "auto", // Window tracking backend passed to the bundled mpv plugin. Auto detects the current platform. Values: auto | hyprland | sway | x11 | macos | windows
    "autoStartSubMiner": true, // Start SubMiner in the background when SubMiner-managed mpv loads a file. Values: true | false
    "pauseUntilOverlayReady": true, // Pause mpv on visible-overlay auto-start until SubMiner signals subtitle tokenization readiness. Values: true | false
    "subminerBinaryPath": "", // Optional SubMiner app binary path passed to the bundled mpv plugin. Leave empty to use the launcher-detected app path.
    "aniskipEnabled": true, // Enable AniSkip intro detection, chapter markers, and the skip-intro key. Values: true | false
    "aniskipButtonKey": "TAB" // mpv key used to skip the detected intro while the skip prompt is visible.
  }, // SubMiner-managed mpv launch and bundled plugin options.

  // ==========================================
  // Jellyfin
  // Optional Jellyfin integration for auth, browsing, and playback launch.
  // Access token is stored in local encrypted token storage after login/setup.
  // jellyfin.accessToken remains an optional explicit override in config.
  // ==========================================
  "jellyfin": {
    "enabled": false, // Enable optional Jellyfin integration and CLI control commands. Values: true | false
    "serverUrl": "", // Base Jellyfin server URL (for example: http://localhost:8096).
    "recentServers": [], // Recently authenticated Jellyfin server URLs shown in setup.
    "username": "", // Default Jellyfin username used during CLI login.
    "defaultLibraryId": "", // Optional default Jellyfin library ID for item listing.
    "remoteControlEnabled": true, // Enable Jellyfin remote cast control mode. Values: true | false
    "remoteControlAutoConnect": true, // Auto-connect to the configured remote control target. Values: true | false
    "autoAnnounce": false, // When enabled, automatically trigger remote announce/visibility check on websocket connect. Values: true | false
    "pullPictures": false, // Enable Jellyfin poster/icon fetching for launcher menus. Values: true | false
    "iconCacheDir": "/tmp/subminer-jellyfin-icons", // Directory used by launcher for cached Jellyfin poster icons.
    "directPlayPreferred": true, // Try direct play before server-managed transcoding when possible. Values: true | false
    "directPlayContainers": [
      "mkv",
      "mp4",
      "webm",
      "mov",
      "flac",
      "mp3",
      "aac"
    ], // Container allowlist for direct play decisions.
    "transcodeVideoCodec": "h264" // Preferred transcode video codec when direct play is unavailable.
  }, // Optional Jellyfin integration for auth, browsing, and playback launch.

  // ==========================================
  // Discord Rich Presence
  // Optional Discord Rich Presence activity card updates for current playback/study session.
  // Uses official SubMiner Discord app assets for polished card visuals.
  // ==========================================
  "discordPresence": {
    "enabled": true, // Enable optional Discord Rich Presence updates. Values: true | false
    "presenceStyle": "default", // Presence card text preset: "default" (clean bilingual), "meme" (Mining and crafting), "japanese" (fully JP), or "minimal". Values: default | meme | japanese | minimal
    "updateIntervalMs": 3000, // Minimum interval between presence payload updates.
    "debounceMs": 750 // Debounce delay used to collapse bursty presence updates.
  }, // Optional Discord Rich Presence activity card updates for current playback/study session.

  // ==========================================
  // Immersion Tracking
  // Enable/disable immersion tracking.
  // Set dbPath to override the default sqlite database location.
  // Policy tuning is available for queue, flush, and retention values.
  // ==========================================
  "immersionTracking": {
    "enabled": true, // Enable immersion tracking for mined subtitle metadata. Values: true | false
    "dbPath": "", // Optional SQLite database path for immersion tracking. Empty value uses the default app data path.
    "batchSize": 25, // Buffered telemetry/event writes per SQLite transaction.
    "flushIntervalMs": 500, // Max delay before queue flush in milliseconds.
    "queueCap": 1000, // In-memory write queue cap before overflow policy applies.
    "payloadCapBytes": 256, // Max JSON payload size per event before truncation.
    "maintenanceIntervalMs": 86400000, // Maintenance cadence (prune + rollup + vacuum checks).
    "retentionMode": "preset", // Retention mode (`preset` uses preset values, `advanced` uses explicit values). Values: preset | advanced
    "retentionPreset": "balanced", // Retention preset when `retentionMode` is `preset`. Values: minimal | balanced | deep-history
    "retention": {
      "eventsDays": 0, // Raw event retention window in days. Use 0 to keep all.
      "telemetryDays": 0, // Telemetry retention window in days. Use 0 to keep all.
      "sessionsDays": 0, // Session retention window in days. Use 0 to keep all.
      "dailyRollupsDays": 0, // Daily rollup retention window in days. Use 0 to keep all.
      "monthlyRollupsDays": 0, // Monthly rollup retention window in days. Use 0 to keep all.
      "vacuumIntervalDays": 0 // Minimum days between VACUUM runs. Use 0 to disable.
    }, // Retention setting.
    "lifetimeSummaries": {
      "global": true, // Maintain global lifetime stats rows. Values: true | false
      "anime": true, // Maintain per-anime lifetime stats rows. Values: true | false
      "media": true // Maintain per-media lifetime stats rows. Values: true | false
    } // Lifetime summaries setting.
  }, // Enable/disable immersion tracking.

  // ==========================================
  // Stats Dashboard
  // Local immersion stats dashboard served on localhost and available as an in-app overlay.
  // Uses the immersion tracking database for overview, trends, sessions, and vocabulary views.
  // ==========================================
  "stats": {
    "toggleKey": "Backquote", // Key code to toggle the stats overlay.
    "markWatchedKey": "KeyW", // Key code to mark the current video as watched and advance to the next playlist entry.
    "serverPort": 6969, // Port for the stats HTTP server.
    "autoStartServer": true, // Automatically start the stats server on launch. Values: true | false
    "autoOpenBrowser": false // Automatically open the stats dashboard in a browser when the server starts. Values: true | false
  } // Local immersion stats dashboard served on localhost and available as an in-app overlay.
}
