2.6 KiB
2.6 KiB
Copilot Instructions
Architecture Overview
Three-component system for remote-controlling a TV display from a mobile phone:
mobile/— Expo React Native app (the "remote control"). Sends commands to the backend.tv/— Vite + React web app (the TV display). Runs in a browser, polls backend every 5s when fullscreen.functions/control/— Python serverless function deployed onshsf-api.reversed.dev. Acts as state store; persists to/app/state.jsonon the container.
API / Backend
All communication goes through a single endpoint:
https://shsf-api.reversed.dev/api/exec/15/1c44d8b5-4065-4a54-b259-748561021329/{route}
Routes (all POST except pull routes which are GET implicitly via fetch):
push_text— body:{ title: string }push_dismiss_textpush_image— body:{ image_b64: string, caption: string }; uploads to MinIO, stores public URLpush_dismiss_imagepull_full— returns full state{ text, image_popup }
Images are stored in MinIO at content2.reversed.dev, bucket tv-control.
Adding a New Content Type
- Add state shape to
DEFAULT_STATEinfunctions/control/main.py - Add
push_*/pull_*handlers inmain(args)in the same file - Add a TypeScript interface and
useStateintv/src/App.tsx; render the popup in the fullscreen branch - Add a page in
mobile/src/pages/ - Register it in the
TABSarray inmobile/App.tsxwithhideInNav: true(prevents it appearing inBottomNav, navigation is triggered programmatically vianavigate())
Mobile App Conventions
- Uses a custom in-memory router (
mobile/src/router.tsx) — no expo-router or react-navigation.Routetype is a string union:"home" | "text" | "image". TABSinmobile/App.tsxis the single source of truth for routes and pages.BottomNavimportsTABSdirectly fromApp.tsx.- Tabs with
hideInNav: trueare reachable only vianavigate(route), not from the bottom bar. - Styling uses React Native
StyleSheetthroughout — no CSS or Tailwind.
TV App Conventions
- Uses Tailwind CSS v4 via
@tailwindcss/vite(notailwind.config.js— config lives invite.config.ts). - Polling only starts when the browser enters fullscreen (
screenStatus === "fullscreen"). - State shape mirrors
DEFAULT_STATEfrom the Python function exactly.
Dev Workflows
# TV display
cd tv && pnpm dev
# Mobile app
cd mobile && pnpm start # Expo Go / dev server
cd mobile && pnpm android # Android emulator
cd mobile && pnpm ios # iOS simulator
Package manager: pnpm for both tv/ and mobile/. Python function has no local runner — deploy changes directly.