v1.8.6

July 29, 2026

Feature release: renamable save state slots, recording sync across Time Machine rewinds, TV Mode screen selection, Wii controller toggle hotkey, RetroAchievements session/ping API, and TCC folder-scope management that stops the repeat Downloads permission prompt at launch.

Highlights

  • Renamable save state slots: Hover a slot to reveal a pencil button and rename it inline, or use the Rename item in the slot/thumbnail context menu. Names persist in a sidecar .meta.json next to each .state file and revert to "Slot N" once emptied. The auto slot (-1) is intentionally non-renamable.
  • Recording stays in sync across rewinds (#30): Video frame appends and audio capture are suspended during rewind/scrub mode, and the rewind window's wall-clock duration is subtracted from the PTS anchors so the recording skips the rewind on both tracks (no frozen frame, no silence gap). An opt-in "Cut rewinds from recording" setting and a "Allow rewind during recording" toggle expose both behaviors; the writer is stopped synchronously on window close before runner.stop() to avoid an audio-capture crash on an unmapped XPC shared-memory pointer.
  • TV Mode screen selection: A new TVModeMainSettingsView centralizes TV Mode settings (behavior, theme, display options, visible collections, gamepad button remapping), and TVModeScreenPickerView lets you choose which connected display to use when entering TV Mode via a new ScreenCatalog.
  • Wii controller toggle hotkey: Attach/detach the Wii controller at runtime through a new hotkey binding. Wii IR settings now use stringified integers for core options, with adjusted defaults in dolphin_libretro_default.json, and synthetic pointer tracking is supported during input capture.
  • RetroAchievements session/ping API: Adds rcheevos_api_init_ping/rcheevos_api_init_start_session for rich-presence updates and session start, plus rcheevos_api_init_login_with_token for token refresh. The settings view surfaces a refresh action for user stats and re-login notifications. Error handling is now a structured RAError type with localized messages.
  • TCC folder-scope management: ROMLibrary now manages TCC permission grants for folder scopes. Combined with the folder bookmark fix in LogManager/LoggerService (resolve with .withSecurityScope, pair startAccessingSecurityScopedResource/stopAccessingSecurityScopedResource around the open log file handle), the app no longer re-prompts for Downloads access on every launch.
  • Hotkey settings revamp: Reset individual hotkey actions or multiple at once to their default bindings. A new HotkeyTab enum organizes App, In-Game, and Special hotkeys, with new sections for Wii controller and Speed & Rewind actions. Gamepad Navigation settings are now reused under Hotkeys rather than living on a separate page.
  • BoxArtService integration: GameCardView and GameDetailView use the shared BoxArtService, with a new forceReplace parameter on downloadAndCache.
  • Per-launch auto-load override: launchGame gains a disableAutoLoadOnStart parameter to skip auto-load for a single launch without changing the global preference.

Detailed Changes

  • feat: add display name for save state slots (#31) — user-renamable slots persisted via sidecar .meta.json, cleared when the slot is fully emptied; SlotInfo gains customName and its Equatable compares it so SwiftUI re-renders on reload
  • fix: keep recording A/V in sync across Time Machine rewinds (#30) — isRewindPaused flag + rewindPtsOffset accumulator, strict monotonic PTS watermarks, audio read position reset on rewind release, synchronous writer stop on windowWillClose
  • feat: opt-in setting to cut Time Machine rewinds from recordings — recording_seamlessRewindCut and timeMachine_rewindDuringRecording (default ON to preserve behavior) with localized OSD hint when rewind is blocked
  • refactor: revert seamless chunk-rotation code (#30) — chunk inflation and trim computation issues forced a revert to the cut-PTS-skip single-writer approach; the MetalCoordinator snapshot fix is preserved so rewind scrub frames stay out of the recording
  • feat: surface TimeMachineBuffer evicted entries + add bufferBasedRecording toggle scaffold — TimeMachineBuffer.push returns evicted entries; the buffer-based pipeline is not yet wired up, recording service still uses cut-PTS-skip
  • chore: leave recording service at working cut-PTS-skip behavior — verified pre-rewind content preserved across rewind boundaries; file length is play-time only
  • feat(tvmode): implement screen selection and settings management — TVModeMainSettingsView, ScreenCatalog, ScreenDescriptor, TVModeScreenPickerView with new localization keys (en/es/pt)
  • feat: add disableAutoLoadOnStart parameter to launchGame function
  • feat(BoxArtService): add forceReplace parameter to downloadAndCache method
  • feat: integrate BoxArtService into GameCardView and GameDetailView for improved box art handling
  • Implement ping and start session API layers for RetroAchievements integration — rcheevos_api_init_ping, rcheevos_api_init_start_session, rcheevos_api_init_login_with_token; RetroAchievementsService manages token refresh and session handling with user notifications and refresh UI
  • feat: enhance RetroAchievements error handling with structured RAError type and localized messages
  • feat: implement TCC permissions management and folder scope handling in ROMLibrary
  • fix: stop re-prompting for Downloads access on every launch — resolve the custom log folder bookmark with .withSecurityScope and pair startAccessingSecurityScopedResource/stopAccessingSecurityScopedResource around the open log file handle
  • feat: Enhance Hotkey Configuration and Settings — per-action and bulk reset of hotkey defaults, new HotkeyTab enum (App/In-Game/Special), Wii controller and Speed & Rewind sections, Gamepad Navigation reused under Hotkeys
  • Refactor logging levels and enhance Wii controller functionality — INFO changed to DEBUG in CoreOverrideBridge/LibretroBridgeImpl/LibretroCallbacks/LibretroGlobals; Wii controller attach/detach toggle hotkey; stringified-int Wii IR core options with adjusted defaults; synthetic pointer tracking during input capture
  • Bump marketing version to 1.8.6 and build number to 29 across project.yml, both Info.plists, and docs/_config.yml