• Shell v2.1: BT picker, RS scroll, MP3 ambient, focus polish
    release / Build Windows .zip (push) Canceled after 0s

    LastSkywalker released this 2026-05-31 22:58:32 +03:00 | 36 commits to main since this release

    Six-item user pass on v0.10.0 polish:

    1. B closes Backups modal. Targeted fix in ShellApp's button hook:
      the Backups overlay is the one shell surface that uses base
      (only listens for keyboard Esc). All other overlays wire
      their own B.

    2. Right-stick scroll. internal/controller now emits a new
      "controller:scroll" {dx, dy} event from ThumbR (50 Hz,
      normalised to [-1..+1], dy flipped to scroll-down-positive).
      Frontend exposes useControllerScroll. ShellSettingsPage and
      GameDrawer wire it (px factor 18 = ~900 px/s at full deflection).

      Also: universal *:focus-visible ring (2 px accent outline) so
      the d-pad walk visibly shows where the cursor went — v0.10.0
      relied on per-component focus styles which were inconsistent.

    3. GameDrawer adopts the roving-focus + RS-scroll model from
      ShellSettingsPage. d-pad / LS walk focusables (↑↓ AND ←→ since
      the drawer is single-column), A clicks the focused element,
      text/number/select inputs skipped, B closes.

    4. Sound: drop pack picker, hardcode "subtle" voice. sound.ts
      simplified to a boolean gs:soundOn (with v0.9 gs:soundPack
      migration). SoundPack / getSoundPack / setSoundPack /
      subscribeSoundPack kept as backward-compat shims so callers
      compile. Settings now shows one checkbox.

    5. Ambient: replace procedural drone with user-supplied MP3 loop.
      Bundled as frontend/src/assets/ambient.mp3 (Vite picks it up).
      At runtime: fetch + decodeAudioData → trim first/last 15 s by
      sample count → bake a seamlessly-looping buffer where the last
      6 s crossfade tail-into-head via sin/cos equal-power ramps
      (sin²+cos²=1, no level dip at the splice). source.loop=true on
      that buffer gives a click-free continuous pad. 4 s fade-in,
      1.5 s fade-out, master cap 0.10.

      Tweaked the frontend-ui.md rule to reflect that an embedded
      audio asset is now used for ambient; the procedural rule still
      stands for nav tones.

    6. Bluetooth picker.

      internal/bluetooth — Win32 BluetoothAPIs.dll wrappers:
      BluetoothFindFirstDevice / FindNextDevice / FindDeviceClose
      BluetoothSetServiceState
      BLUETOOTH_DEVICE_INFO is 560 bytes on x64 per MSDN — Go natural
      alignment with the documented dwSize+pad layout matches.
      runtime.LockOSThread while holding the find-handle.

      "Connect" = enable all known audio profile GUIDs (A2DP sink,
      AVRCP, HFP/HSP gateways). Headphones expose different
      combinations; an unsupported profile returns
      ERROR_SERVICE_DOES_NOT_EXIST which we treat as a clean skip. As
      long as at least one service flipped, return nil; otherwise
      first-non-skip-error wins.

      sync.Mutex serialises the slow blocking calls
      (BluetoothSetServiceState can block ~30 s while the radio
      negotiates with the headset).

      Pairing is OUT of scope — OpenWindowsBluetoothSettings punts to
      fsquirt.exe for "add a new device".

      App.ListBluetoothDevices / Connect / Disconnect /
      OpenWindowsBluetoothSettings exposed. Frontend
      BluetoothPicker.tsx — full-screen list, audio devices sorted
      first, A toggles, X/R refreshes, B closes. Wired into
      DevicesMenu (BT tile no longer disabled) and Settings (📶
      button now active in shell mode).

    ADR: .claude/decisions/0026-bluetooth-picker-rs-scroll-mp3-ambient.md

    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

    Downloads