# ToneCast Hotkeys (Desktop)

Cross-platform desktop app that registers **OS global hotkeys** for digits **0–9** and triggers the same media bindings as the ToneCast dashboard (Shift+hotkeys), via HTTP — **without injecting keystrokes** into the focused app.

Supported: **macOS**, **Linux**, **Windows** (Electron).

## Features

- **ToneCast account sign-in** (email/password → same `desktop_login` / API token as ToneCast Desktop 1.1.0)
- Cloud (tonecast.net) requires being signed in before enabling hotkeys
- On/Off switch — when off, hotkeys do nothing
- Configurable modifiers: Shift (default), Ctrl, Alt/Option, Cmd/Super, and combos (e.g. Cmd+Shift, Ctrl+Shift)
- Plays/casts bound media immediately through `trigger_media_hotkey`
- Does not steal focus when a hotkey fires
- Tray icon — close the window and hotkeys keep working
- Test buttons for slots 0–9

## Requirements

- Node.js 18+ (20 recommended)
- ToneCast account (for cloud) or local ToneCast Desktop token (for `127.0.0.1:8787`)
- Dashboard bindings assigned (right-click photo / sound pad → Shift+0–9)

## Install & run (all platforms)

```bash
cd desktop-hotkeys
npm install
npm start
```

### Cloud (tonecast.net)

1. Assign bindings in the ToneCast dashboard
2. Open ToneCast Hotkeys
3. **Sign in** with your tonecast.net email and password (token is filled automatically)
4. Choose modifiers, turn **Enabled** on
5. Press your modifier+digit from any app

### Local ToneCast Desktop

1. Set **Server URL** to `http://127.0.0.1:8787`
2. Under **Advanced**, paste the Desktop API token (Downloads page or Desktop settings)
3. Enable hotkeys

### Modifier tips

| Want | Check |
|------|--------|
| Same as dashboard | **Shift** only |
| Avoid conflicting with typing | **Ctrl+Shift** or **Alt+Shift** |
| Mac-friendly | **Cmd+Shift** |

At least one modifier is recommended. If none are selected, the app falls back to Shift.

### macOS permissions

On first enable, macOS may require **Accessibility** (and sometimes **Input Monitoring**) permission so Electron can register global shortcuts:

**System Settings → Privacy & Security → Accessibility** → allow **ToneCast Hotkeys**.

### Linux notes

- Global shortcuts need an active graphical session (X11 or Wayland with portal support).
- On some Wayland compositors, Electron `globalShortcut` may be limited; X11 / XWayland is the most reliable path for MVP.

### Windows notes

- Run normally; no admin required for global shortcuts.
- If a combo is already owned by another app, registration fails for that accelerator — try a different modifier set.

## Prebuilt downloads (1.1.0)

From [Downloads](https://tonecast.net/downloads.html#desktop-hotkeys):

| Platform | Package |
|----------|---------|
| Linux x64 | `streamassist-desktop-hotkeys-linux-x64-1.1.0.tar.gz` → extract → `./streamassist-hotkeys` |
| macOS | `streamassist-desktop-hotkeys-mac-1.1.0.zip` → `streamassist-hotkeys.app` (unsigned; right-click → Open) |
| Windows | `streamassist-desktop-hotkeys-win-x64-1.1.0.zip` → `win-unpacked/streamassist-hotkeys.exe` |

## Build installers

```bash
npm install
npm run dist          # current platform
npm run dist:linux    # unpacked dir (then tar)
# Cross-build from Linux (unsigned dirs; then zip):
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --mac --x64 --dir
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --win --x64 --dir
# Native installers:
npm run dist:mac      # dmg + zip (best on macOS)
npm run dist:win      # NSIS + portable (best on Windows; may need wine when cross-building)
```

Artifacts land in `dist/`.

This Linux host can usually produce **Linux unpacked** builds. Mac and Windows packages should be built on those OSes (or CI) for reliable signing and native targets.

## How it works

1. Dashboard right-click assign dual-writes bindings to the server (`media_hotkeys` table)
2. Sign-in calls `POST api.php?action=desktop_login` and stores the account `desktop_api_token`
3. This app registers OS-level shortcuts with Electron `globalShortcut` (suppresses the combo from other apps when registration succeeds)
4. On press → `POST api.php?action=trigger_media_hotkey` with `{ slot }` and `X-ToneCast-Token` / `X-StreamAssist-Token`
5. Server plays/casts the bound sound or media on the chosen screen(s)

No keystrokes are sent to the focused application.

## API used

| Action | Purpose |
|--------|---------|
| `desktop_login` | Email/password → desktop API token |
| `get_media_hotkeys` | List slot bindings |
| `set_media_hotkey` | Assign a slot (dashboard) |
| `clear_media_hotkey` | Clear a slot / item |
| `trigger_media_hotkey` | Play/cast slot (requires token) |

## Config storage

Settings are saved under the Electron userData folder (`config.json`):

- macOS: `~/Library/Application Support/streamassist-hotkeys/`
- Linux: `~/.config/streamassist-hotkeys/`
- Windows: `%APPDATA%\streamassist-hotkeys\`

## Project layout

```
desktop-hotkeys/
  main.js          # Electron main: globalShortcut + HTTP + auth
  preload.js
  renderer/        # Dark config UI
  assets/icon.png
  package.json
```
