Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01As1S7fpDqEDnZxcGbSSoUW
felt-tray
The Windows desktop shell for Felt.
Lives in the system tray, owns the global push-to-talk hotkey, and is the
relay between felt-core (the brain), Mario, and felt-overlay (the VRM
desktop companion): mic in, felt-core's reply out as a toast + spoken audio,
and any resolved animation tag forwarded on to felt-overlay to act on.
Originally planned as a Rust/Tauri port of the Linux prototype
(felt-dashboard's src-tauri/); rebuilt in Python instead after the
desktop's Linux→Windows migration (Mario, 2026-09-20: "if you think another
language would be better, feel free to swap, i'm not a die hard rust fan").
felt-dashboard now builds a plain static frontend with no native shell of
its own; this project hosts it.
What it does
- Tray icon (
pystray): Show Felt, keybind rebinding, a Celsius/Fahrenheit toggle mirrored to felt-core's/v1/settings/temperature-unit, Quit. - Global hotkeys (
keyboard): one toggles voice push-to-talk, one shows the chat window ready to type. Rebindable from the tray menu, persisted back to.env. - Push-to-talk flow (the primary path, no window involved): native mic
capture (
sounddevice) →felt-core'sPOST /v1/voice-chat→ the reply plays back as audio (winsound) and shows as a native toast (winotify) — Mario, 2026-09-20: "replace output instead of opening the thing, just toast". Any resolvedanimation_tagin the response is relayed on to felt-overlay. - Chat window (
pywebview, WebView2): the hosted Felt app (felt-dashboard atfelt.clockmost.com/app/, behind SSO; WP-42.2), the same app as on the phone. Lines typed there go through the tray (bridge.py'stext_chat→ felt-core/v1/text-chat), so she speaks the reply like a spoken turn. Cookies persist (%LOCALAPPDATA%\Felt\webview), so SSO doesn't ask again after every restart. Closing it hides it; only the tray's Quit actually exits. - felt-overlay handoff: felt-tray does not launch or talk to
felt-overlay directly — that ownership moved to
felt-animationin WP-08 (there's nosrc/felt_tray/overlay.pyin this repo any more). felt-tray's own reply flow (mic capture, screen-reaction) posts the resolved text/tag/audio to felt-animation'sPOST {FELT_ANIMATION_URL} /reply_ready(app.py,timing.py); felt-animation runs lipsync, picks a reaction, and relays it to felt-overlay over its own TCP protocol.LaunchFelt.ps1is what actually startsFeltOverlay.exe(via felt-animation'soverlay.py) — see the umbrella'sdocs/architecture/umbrella-scripts.mdand felt-animation's ownARCHITECTURE.mdfor that wire format.
Setup
uv sync
cp .env.example .env # fill in VITE_FELT_CORE_URL at minimum
The chat window needs the desktop to reach felt.clockmost.com (LAN or
VPN); the first open asks for the SSO login once.
uv run felt-tray
Not yet built
- felt-core's fuller tag→clip resolution (fallback broadening, specific/generic weighting, repeat-avoidance) — felt-tray just forwards whatever tag felt-core already resolved; see my private design notes for that.