8574424fb7
Lint & Test / test (push) Successful in 2m10s
Adds a native Android TV application that runs the full LedGrab Python server in-process via Chaquopy. Captures the TV box screen using the MediaProjection API and exposes the existing web UI on the device's local network — users configure via phone/tablet browser. Android (new /android/ module): - Kotlin shell: MainActivity, CaptureService (foreground service), ScreenCapture (MediaProjection + ImageReader), PythonBridge (Chaquopy). - Polished Leanback-themed UI with QR code for easy web UI access. - AGP 8.9 + Chaquopy 17 + Gradle 8.11 (avoids the AGP 8.7 thread-lock bug). - Pre-built pydantic-core wheels for arm64-v8a, x86_64, x86 cross-compiled with maturin + Android NDK, linked against Chaquopy's libpython3.11.so. Python server platform guards: - New utils/platform.py with is_android()/is_windows()/is_linux() helpers. - Guard every top-level import of desktop-only packages (mss, psutil, sounddevice, pyserial, PyAudioWPatch, etc.) with try/except ImportError. - Android-incompatible calls gated with None-checks so the server runs on reduced capabilities on Android (no CPU/RAM metrics, no mss displays). - utils/image_codec.py gains a Pillow fallback for resize + JPEG encode when cv2 is unavailable; all internal cv2.resize callers migrated. - New android_entry.py start_server/stop_server invoked from Kotlin. - get_displays API falls back to best available engine when mss fails. New capture engines: - MediaProjectionEngine: receives RGBA frames pushed from Kotlin through a thread-safe queue; caches last frame for static-screen previews. - ScrcpyClientEngine: optional H.264 streaming via scrcpy-client library (priority 10, overrides the ADB-screencap engine when installed). Frontend: - Tab loaders previously required an apiKey; now correctly treat "auth disabled" as authenticated (Android has no auth by default). - Re-trigger the active tab's loader after loadServerInfo resolves authRequired, since initTabs runs earlier. - Add i18n keys for the demo / mediaprojection / scrcpy_client engines. Docs: - TODO.md: follow-ups for multi-ABI wheel rebuilds, CI pipeline, USB serial LED controllers, root-only capture, perf metrics abstraction. - CLAUDE.md: Android dependency sync policy (pip --exclude doesn't exist). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
107 lines
6.7 KiB
Markdown
107 lines
6.7 KiB
Markdown
# Claude Instructions for LedGrab
|
|
|
|
## Code Search
|
|
|
|
**If `ast-index` is available, use it as the PRIMARY code search tool.** It is significantly faster than grep and returns structured, accurate results. Fall back to grep/Glob only when ast-index is not installed, returns empty results, or when searching regex patterns/string literals/comments.
|
|
|
|
**IMPORTANT for subagents:** When spawning Agent subagents (Plan, Explore, general-purpose, etc.), always instruct them to use `ast-index` via Bash for code search instead of grep/Glob. Example: include "Use `ast-index search`, `ast-index class`, `ast-index usages` etc. via Bash for code search" in the agent prompt.
|
|
|
|
```bash
|
|
ast-index search "Query" # Universal search
|
|
ast-index class "ClassName" # Find class/struct/interface definitions
|
|
ast-index usages "SymbolName" # Find all usage sites
|
|
ast-index symbol "FunctionName" # Find any symbol
|
|
ast-index callers "FunctionName" # Find all call sites
|
|
ast-index outline "path/to/File.py" # Show all symbols in a file
|
|
ast-index changed --base master # Show symbols changed in current branch
|
|
```
|
|
|
|
## Git Commit and Push Policy
|
|
|
|
**NEVER commit or push without explicit user approval.** Wait for the user to review changes and explicitly say "commit" or "push". Completing a task, "looks good", or "thanks" do NOT count as approval. See the system-level instructions for the full commit workflow.
|
|
|
|
## Auto-Restart and Rebuild Policy
|
|
|
|
- **Python code changes** (`server/src/` excluding `static/`): Auto-restart the server. See [contexts/server-operations.md](contexts/server-operations.md) for the restart procedure.
|
|
- **Frontend changes** (`static/js/`, `static/css/`): Run `cd server && npm run build` to rebuild the bundle. No server restart needed.
|
|
|
|
## Project Structure
|
|
|
|
- `/server` — Python FastAPI backend (see [server/CLAUDE.md](server/CLAUDE.md))
|
|
- `/android` — Android TV app (Kotlin shell + embedded Python via Chaquopy)
|
|
- `/contexts` — Context files for Claude (frontend conventions, graph editor, Chrome tools, server ops, demo mode)
|
|
|
|
## Android Dependency Sync (CRITICAL)
|
|
|
|
The Android app (`android/app/build.gradle.kts`) installs the server package with `--no-deps` and lists Android-compatible dependencies **explicitly** in the Chaquopy `pip {}` block. This is because `server/pyproject.toml` includes desktop-only packages (mss, psutil, sounddevice, etc.) that have no Android wheels.
|
|
|
|
**When adding a new dependency to `server/pyproject.toml`:**
|
|
|
|
1. If the package is **pure Python or has Chaquopy wheels** (check [Chaquopy PyPI](https://chaquo.com/pypi-13.1/)), also add it to `android/app/build.gradle.kts` in the `pip { install(...) }` block
|
|
2. If the package is **desktop-only** (native C/Rust extension without Android support), do NOT add it to `build.gradle.kts` — and guard its import with `try/except ImportError` in Python code
|
|
3. If unsure, check Chaquopy's package index first
|
|
|
|
**Incident context:** Chaquopy's pip runs on the build machine (Windows), not on Android. Platform markers like `sys_platform != 'linux'` evaluate against the BUILD host, not the target device. `pip install --exclude` does not exist. The only reliable way to exclude packages is to not list them.
|
|
|
|
## Context Files
|
|
|
|
| File | When to read |
|
|
| ---- | ------------ |
|
|
| [contexts/frontend.md](contexts/frontend.md) | HTML, CSS, JS/TS, i18n, modals, icons, bundling |
|
|
| [contexts/graph-editor.md](contexts/graph-editor.md) | Visual graph editor changes |
|
|
| [contexts/server-operations.md](contexts/server-operations.md) | Server restart, startup modes, demo mode |
|
|
| [contexts/chrome-tools.md](contexts/chrome-tools.md) | Chrome MCP tool usage for testing |
|
|
| [contexts/ci-cd.md](contexts/ci-cd.md) | CI/CD pipelines, release workflow, build scripts |
|
|
| [Gitea Python CI/CD Guide](https://git.dolgolyov-family.by/alexei.dolgolyov/claude-code-facts/src/branch/main/gitea-python-ci-cd.md) | Reusable CI/CD patterns: Gitea Actions, cross-build, NSIS, Docker |
|
|
| [server/CLAUDE.md](server/CLAUDE.md) | Backend architecture, API patterns, common tasks |
|
|
|
|
## Task Tracking via TODO.md
|
|
|
|
Use `TODO.md` in the project root as the primary task tracker. **Do NOT use the TodoWrite tool** — all progress tracking goes through `TODO.md`.
|
|
|
|
## Documentation Lookup
|
|
|
|
**Use context7 MCP tools for library/framework documentation lookups** (FastAPI, OpenCV, Pydantic, yt-dlp, etc.) instead of relying on potentially outdated training data.
|
|
|
|
## Data Migration Policy (CRITICAL)
|
|
|
|
**NEVER rename a storage file path, store key, entity ID prefix, or JSON field name without writing a migration.** User data lives in JSON files under `data/`. If the code starts reading from a new filename while the old file still has user data, THAT DATA IS SILENTLY LOST.
|
|
|
|
When renaming any storage-related identifier:
|
|
|
|
1. **Add migration logic in `BaseJsonStore.__init__`** (or the specific store) that detects the old file/key and migrates data to the new name automatically on startup
|
|
2. **Log a clear warning** when migration happens so the user knows
|
|
3. **Keep the old file as a backup** after migration (rename to `.migrated` or similar)
|
|
4. **Test the migration** with both old-format and new-format data files
|
|
5. **Document the migration** in the commit message
|
|
|
|
This applies to: file paths in `StorageConfig`, JSON root keys (e.g. `picture_targets` → `output_targets`), entity ID prefixes (e.g. `pt_` → `ot_`), and any field renames in dataclass models.
|
|
|
|
**Incident context:** A past rename of `picture_targets.json` → `output_targets.json` was done without migration. The app created a new empty `output_targets.json` while the user's 7 targets sat unread in the old file. Data was silently lost.
|
|
|
|
## UI Component Rules (CRITICAL)
|
|
|
|
**NEVER use plain HTML `<select>` elements.** The project uses custom selector components:
|
|
- **IconSelect** (icon grid) — for predefined items (effect types, palettes, easing modes, animation types)
|
|
- **EntitySelect** (entity picker) — for entity references (sources, templates, devices)
|
|
|
|
Plain HTML selects break the visual consistency of the UI.
|
|
|
|
## Pre-Commit Checks (MANDATORY)
|
|
|
|
Before every commit, run the relevant checks and fix any issues:
|
|
|
|
- **Python changes**: `cd server && ruff check src/ tests/ --fix`
|
|
- **TypeScript changes**: `cd server && npx tsc --noEmit && npm run build`
|
|
- **Both**: Run both checks
|
|
- **Always run tests**: `cd server && py -3.13 -m pytest tests/ --no-cov -q` — all tests MUST pass before committing. Do NOT commit code that fails tests.
|
|
|
|
Do NOT commit code that fails linting or tests. Fix the issues first.
|
|
|
|
## General Guidelines
|
|
|
|
- Always test changes before marking as complete
|
|
- Follow existing code style and patterns
|
|
- Update documentation when changing behavior
|
|
- Never make commits or pushes without explicit user approval
|