**Phase 01 final verification pass — complete, 100% green** - Verified all 4 tasks done (files in `complete/`); implementation review: key router in `watch.js`, share-page router, CSS focus/selection affordances, `?` overlay, pointer-free E2E suite — all sound. - Gates: `ruff check` + `ruff format --check` ✅ · `pyright` 0 errors ✅ · `uv run pytest` → 223 passed, 2 skipped (realdisplay), 1 pre-existing FastAPI deprecation warning · `MIRROR_REALDISPLAY=1 pytest -m realdisplay` → 2 passed ✅ · `pytest -m e2e` → 51 passed (incl. `test_keyboard_wall.py`, which a meta-test keeps pointer-free) · `node --check` on all static JS ✅. - Completion criteria: all 6 ✅ (j/k+Enter, f/g/n/p/1–9/m/?/Esc keyboard-only E2E; no hover-only controls via CSS guard + focus-ring E2E; `#name` text-safe; e2e/realdisplay green; key map documented in README "Kiosk client" and `docs/kiosk-silverblue.md`). - README fixed: added missing env vars `MIRROR_MAX_BITRATE`, `MIRROR_PING_INTERVAL`, `DEBUGPY_WAIT` to the config table; added the one-time `rpm-ostree` layering note before the kiosk install snippet. - AGENTS.md fixed: removed stale "roadmap is intentionally empty" parenthetical (3 phases pending); coverage ~94% → ~95%. No content added. - **Unresolved, needs your call:** README/SECURITY.md say `mirror.reeseapps.com`; `docs/kiosk-silverblue.md` (reference-client naming table with real IPs) says `mirror.reeselink.com`. I couldn't verify which is the live deployment domain, so I changed neither — say the word and I'll unify. - Next pending phase: `02_sharp_capture`.
74 lines
1.7 KiB
TOML
74 lines
1.7 KiB
TOML
[project]
|
|
name = "mirror"
|
|
version = "0.1.0"
|
|
description = "Device-agnostic, browser-only screen mirroring for the local network."
|
|
readme = "README.md"
|
|
requires-python = ">=3.13"
|
|
license = { text = "MIT" }
|
|
authors = [{ name = "Reese Apps" }]
|
|
dependencies = [
|
|
"fastapi>=0.115",
|
|
"uvicorn[standard]>=0.30",
|
|
"python-dotenv>=1.0",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"debugpy>=1.8",
|
|
"pytest>=8.3",
|
|
"pytest-asyncio>=0.24",
|
|
"pytest-cov>=5.0",
|
|
"httpx>=0.27",
|
|
"ruff>=0.6",
|
|
"pyright>=1.1.380",
|
|
"playwright>=1.47",
|
|
"pytest-timeout>=2.3",
|
|
]
|
|
|
|
[project.scripts]
|
|
mirror = "mirror.__main__:main"
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["src/mirror"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py313"
|
|
src = ["src", "tests"]
|
|
# The phase tree is harness-managed planning documents, not code: ruff 0.17+
|
|
# formats the code blocks inside them, and a phase author's snippet must not
|
|
# be able to fail the project gate (nor may an executor reformat a phase file
|
|
# to fix it — the harness owns those paths).
|
|
extend-exclude = [".agents"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM", "ARG", "RUF"]
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
addopts = "-ra --strict-markers --cov=mirror --cov-report=term-missing"
|
|
timeout = 60
|
|
asyncio_default_fixture_loop_scope = "function"
|
|
asyncio_mode = "auto"
|
|
markers = [
|
|
"e2e: browser end-to-end tests (require Playwright browsers)",
|
|
"realdisplay: end-to-end tests that capture a real X11 display",
|
|
]
|
|
|
|
[tool.coverage.run]
|
|
source = ["src/mirror"]
|
|
|
|
[tool.pyright]
|
|
include = ["src"]
|
|
pythonVersion = "3.13"
|
|
typeCheckingMode = "standard"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest-timeout>=2.4.0",
|
|
]
|