Mobile companion app + paired-device backend

New iOS-first companion (Expo SDK 54 + NativeWind v4) with three tabs:
Captures (the hero — floating gold mic, live mic-meter waveform,
expand-row playback), Generate (profile picker + speak + autoplay +
recent), and Voices (searchable profile list).

Pairing (V0): backend mints a one-time token, mobile scans/pastes the
voicebox:// URL, server returns a long-lived bearer it stores only as a
SHA-256 hash. Bearer-or-loopback auth applied to every user-data router
so binding 0.0.0.0 doesn't leak existing endpoints. Loopback callers
(the desktop app) keep their friction-free access.

Desktop Settings → Mobile pane: live host picker (LAN / Tailscale auto-
detected via the App-bundle binary path on macOS), QR rendering,
5-minute expiry countdown, copyable URL fallback, paired-device list
with revoke. Auto-closes when a new device pairs.

just dev now binds the backend to 0.0.0.0 so paired phones can reach
it — and just setup-python pins mlx-audio==0.4.1 + mlx-lm so fresh
Apple Silicon worktrees get a working STT path on first install.
This commit is contained in:
James Pine
2026-04-25 17:09:32 -07:00
parent 2bcb98d1a8
commit f4d21504e3
49 changed files with 5229 additions and 30 deletions
+8
View File
@@ -18,6 +18,7 @@ import { GenerationPage } from '@/components/ServerTab/GenerationPage';
import { GpuPage } from '@/components/ServerTab/GpuPage';
import { LogsPage } from '@/components/ServerTab/LogsPage';
import { MCPPage } from '@/components/ServerTab/MCPPage';
import { MobilePage } from '@/components/ServerTab/MobilePage';
import { SettingsLayout } from '@/components/ServerTab/ServerTab';
import { Sidebar } from '@/components/Sidebar';
import { StoriesTab } from '@/components/StoriesTab/StoriesTab';
@@ -166,6 +167,12 @@ const settingsMCPRoute = createRoute({
component: MCPPage,
});
const settingsMobileRoute = createRoute({
getParentRoute: () => settingsRoute,
path: '/mobile',
component: MobilePage,
});
const settingsGpuRoute = createRoute({
getParentRoute: () => settingsRoute,
path: '/gpu',
@@ -212,6 +219,7 @@ const routeTree = rootRoute.addChildren([
settingsGenerationRoute,
settingsCapturesRoute,
settingsMCPRoute,
settingsMobileRoute,
settingsGpuRoute,
settingsLogsRoute,
settingsChangelogRoute,