diff --git a/.agents/phases/todo/04_reachable_share_cta/00_phase.md b/.agents/phases/complete/04_reachable_share_cta/00_phase.md similarity index 100% rename from .agents/phases/todo/04_reachable_share_cta/00_phase.md rename to .agents/phases/complete/04_reachable_share_cta/00_phase.md diff --git a/.agents/phases/todo/04_reachable_share_cta/01_one_cell_stage.md b/.agents/phases/complete/04_reachable_share_cta/01_one_cell_stage.md similarity index 100% rename from .agents/phases/todo/04_reachable_share_cta/01_one_cell_stage.md rename to .agents/phases/complete/04_reachable_share_cta/01_one_cell_stage.md diff --git a/.agents/phases/todo/04_reachable_share_cta/02_size_the_stage_for_real_viewports.md b/.agents/phases/complete/04_reachable_share_cta/02_size_the_stage_for_real_viewports.md similarity index 100% rename from .agents/phases/todo/04_reachable_share_cta/02_size_the_stage_for_real_viewports.md rename to .agents/phases/complete/04_reachable_share_cta/02_size_the_stage_for_real_viewports.md diff --git a/.agents/phases/todo/04_reachable_share_cta/03_reachable_cta_e2e.md b/.agents/phases/complete/04_reachable_share_cta/03_reachable_cta_e2e.md similarity index 100% rename from .agents/phases/todo/04_reachable_share_cta/03_reachable_cta_e2e.md rename to .agents/phases/complete/04_reachable_share_cta/03_reachable_cta_e2e.md diff --git a/.agents/phases/todo/04_reachable_share_cta/04_css_guard_and_docs.md b/.agents/phases/complete/04_reachable_share_cta/04_css_guard_and_docs.md similarity index 100% rename from .agents/phases/todo/04_reachable_share_cta/04_css_guard_and_docs.md rename to .agents/phases/complete/04_reachable_share_cta/04_css_guard_and_docs.md diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.err b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.md b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.md new file mode 100644 index 0000000..6fb9a0b --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.md @@ -0,0 +1,23 @@ +All verification is complete. The pending working-tree changes are exactly the phase 04 work (CSS, tests, the README narrow-screen line, the AGENTS.md "Two→Three pages" fix) — all accurate. + +## Final report + +**Verified (final pass — all 4 task files present in `complete/`):** +- Gate 1: `uv run ruff check . && uv run ruff format --check .` → all checks passed, 29 files formatted +- Gate 2: `uv run pyright` → 0 errors +- Gate 3: `uv run pytest` → **269 passed, 4 skipped** (realdisplay-gated), coverage 95% +- Gate 4: `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` → **3 passed, 1 skipped** (host delivers no audio source — documented in module docstring) + +**Completion criteria:** +1. `#start` elementFromPoint at the 11 required sizes — ✅ `TestIdleShareButtonReachable` sweeps 12 viewports (superset); 12/12 passed +2. `#stop` clickable while sharing — ✅ `TestLiveStopButtonReachable` (320×568, 390×844, 1280×450) passed, incl. placeholder-hidden + livetag assertions +3. Single-cell stage guard — ✅ `test_the_share_stage_is_one_cell` + `test_the_stage_has_a_floor_and_a_ceiling` passed; CSS has `grid-template: 1fr / 1fr`, `.stage > * { grid-area: 1 / 1 }`, `min-height: 264px`, `max-height: min(70svh, 640px)` + vh fallback, `.placeholder { max-height: 100%; overflow: auto }`, `align-content: safe center` +4. Live video fills stage, rounded corners, `#livetag` top-left — ✅ verified by live-browser probe: video fills stage (decoded), stage radius 14px, livetag top-left, placeholder hidden +5. README accurate, AGENTS.md current, suite 100% green — ✅ +6. Phases 01–03 unchanged — ✅ `test_keyboard_wall` + `test_audio_share` + `test_sender_fidelity`: 48 passed + +**Documentation:** README.md reviewed in full (page table, quickstart, all 12 config defaults vs `config.py`, CLI flags, secure-context/wayland/audio sections, kiosk key table vs `watch.js`/`share.js`, dev commands) — **consistent, no fixes needed**. AGENTS.md verified statement-by-statement (commands, routes, test names, browser/realdisplay flags) — **no stale statements; no changes needed** (the pending "Two→Three pages" fix is correct). + +**Decisions:** criterion 4 had no dedicated test, so verified directly with a throwaway probe reusing the e2e harness (script in /tmp, not committed). No live infrastructure touched. No defects found in prior work. + +**Next pending phase:** none — `todo/` is empty of other phases. diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.md.status b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.validate b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.validate new file mode 100644 index 0000000..b687bb6 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__00_phase.a1.validate @@ -0,0 +1,34 @@ +........................................................................ [ 26%] +.................ssss................................................... [ 52%] +........................................................................ [ 79%] +......................................................... [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 11 94% 108, 113, 117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 32 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:281: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:321: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:364: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:446: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +269 passed, 4 skipped, 1 warning in 68.73s (0:01:08) +All checks passed! +29 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.err b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.md b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.md new file mode 100644 index 0000000..a250286 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.md @@ -0,0 +1,10 @@ +Everything is verified and green. Final report: + +**Phase 04 / Task 01 — one-cell stage: complete.** + +- **Implemented**: `src/mirror/static/app.css` `.stage` now declares `grid-template: 1fr / 1fr` (one cell) plus `.stage > * { grid-area: 1 / 1; }`, so `#preview`, `#placeholder`, `#livetag` overlap in one cell; invariant comment added at the rule. `.livetag` stays absolute, `.stage video` untouched, `share.js`/`mirror.html` untouched. Diff confined to the `/* share */` block. +- **Instrumented browser check** (Chromium `channel="chromium"`, `http://127.0.0.1`): idle `#placeholder` topOffset now 1 @320×568 and 187.4 @1280×800 (centred, no longer the constant 151 px row); `#start` bottom 37 px below stage at 320 (was 187) and fully inside at 1280 with `elementFromPoint` = `BUTTON#start`; live state A/B-probed against old CSS — geometry byte-identical at both sizes (placeholder hidden, `#stop` clickable, video + `#livetag` unchanged → no visual regression). +- **Screenshots**: `docs/screenshots/phase-04/` — `idle-1280x800.png`, `idle-390x844.png`, `live-1280x800.png` (visually verified: CTA centred, live video fills stage, LIVE top-left). +- **Gates**: `uv run ruff check . && uv run ruff format --check .` clean · `uv run pyright` 0 errors · `uv run pytest` **245 passed, 4 skipped** (realdisplay-only) · `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` **3 passed, 1 skipped** (pre-existing documented host-audio skip). Coverage 95% (floor >90%). +- **Docs**: no README/AGENTS.md changes — README makes no claim about the share-page layout that this fix alters (no stale statement; the one pre-existing warning is a Starlette dependency deprecation, untouched). +- **Next pending task**: `02_size_the_stage_for_real_viewports.md`. diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.md.status b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.validate b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.validate new file mode 100644 index 0000000..0be2212 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__01_one_cell_stage.a1.validate @@ -0,0 +1,34 @@ +...................................................................ssss. [ 28%] +........................................................................ [ 57%] +........................................................................ [ 86%] +................................. [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 11 94% 108, 113, 117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 32 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:281: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:321: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:364: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:446: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +245 passed, 4 skipped, 1 warning in 66.61s (0:01:06) +All checks passed! +28 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.err b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.md b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.md new file mode 100644 index 0000000..e56845a --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.md @@ -0,0 +1,16 @@ +All green. Final state verified — everything complete: + +**Task 02 complete.** Report: + +**Implemented** +- `app.css` (entirely inside the `/* share */` block): `.stage` floor `min-height: 264px`, ceiling `max-height: min(70vh, 640px)` + `min(70svh, 640px)` (vh fallback), `align-content: safe center`; `.stage .placeholder` escape hatch `max-height: 100%; overflow: auto`; `aspect-ratio: 4/3` at ≤900px, `auto` + 16px padding at ≤480px +- Two fixes beyond the literal task: (1) media queries placed *after* the base rules — before that, equal-specificity source order made them lose; (2) `width: 100%` on `.stage` — a stretched grid item with aspect-ratio gets its *width* recomputed from a max-height-clamped height (stage shrank to 1024px, 534px gap beside the panel); a definite width keeps the full column and the video letterboxes, per the task's ASSUMPTION +- `tests/e2e/test_live_mirror.py::TestStageSizing`: the 6 measured failure viewports (button inside stage + inside viewport, floor ≥263, stage bottom on-screen) + 1920×1080 ceiling (≤641px, Session panel reachable) — in `test_live_mirror.py` since `test_share_layout.py` belongs to task 03 + +**Before → after** (measured, Chromium over 127.0.0.1): 320×568 stage 180→264, `#start` clipped→263–315 inside; 900×480 542→336 (on-screen); 1280×450 564→315; 1920×1080 964→640, panel bottom 499 < 1080. Test verified **red before** the fix (6/7 viewports + ceiling fail) and green after. + +**Gates**: `uv run ruff check . && uv run ruff format --check .` clean · `uv run pyright` 0 errors · `uv run pytest` 252 passed, 4 skipped (realdisplay-gated) · `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` 3 passed, 1 skipped (documented env-gated audio) · `pytest tests/test_app.py -m "not e2e"` 68 passed · coverage 95% (no new src code; phase is CSS-only per 00_phase.md) + +**Docs**: README.md — added the sharer-facing narrow-screen line (320px viewport, CTA always on screen). AGENTS.md — no stale statements, no edit. + +**Next pending task**: `03_reachable_cta_e2e` — the 12-viewport `elementFromPoint` sweep in `tests/e2e/test_share_layout.py`, idle + live `#stop`. diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.md.status b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.validate b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.validate new file mode 100644 index 0000000..fa41c87 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__02_size_the_stage_for_real_viewports.a1.validate @@ -0,0 +1,34 @@ +........................................................................ [ 28%] +..ssss.................................................................. [ 56%] +........................................................................ [ 84%] +........................................ [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 12 94% 108, 113, 116-117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 33 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:281: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:321: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:364: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:446: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +252 passed, 4 skipped, 1 warning in 66.55s (0:01:06) +All checks passed! +28 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.err b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.md b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.md new file mode 100644 index 0000000..f812484 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.md @@ -0,0 +1,17 @@ +All green. Report: + +**Implemented** +- `tests/e2e/test_share_layout.py`: `#start` hit-test sweep over the 12 task viewports (verbatim `elementFromPoint` probe; asserts `disabled`, `hit_is_button` with `hit` named in the failure message, `in_stage`, `in_viewport`, `no_h_scroll`) plus live-state sweep at 320×568 / 390×844 / 1280×450 asserting `#stop` hit-testable, `#placeholder` hidden, `#livetag` visible inside the stage — reusing `base_url`/`browser`/`start_sharing` from `tests/e2e/conftest.py`. +- Pre-fix verification (temporary revert to HEAD CSS, then restored): **7/12 idle viewports fail** — 320×568 (`hit: H2`), 360×640 (`hit: #name`), 390×844 (`hit: LABEL`), 430×932 (`hit: H2`), 480×800 (`hit: MAIN`), 900×480 (`hit: null`), 1280×450 (`hit: null`); 700×900, 900×700, 1024×600, 1280×800, 1920×1080 pass. Matches the 00_phase diagnosis (480×800's hit landed on `MAIN` this run vs `#share-link` in the original table — same clip, culprit named in output). Please fold these failing widths into the phase commit message. + +**Gates (all green)** +- `uv run ruff check . && uv run ruff format --check .` — All checks passed; 29 files formatted +- `uv run pyright` — 0 errors +- `uv run pytest` — 267 passed, 4 skipped (realdisplay gate), 95% coverage +- `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` — 3 passed, 1 skipped (host offers no audio source; pre-existing, documented in the test) + +**Docs** — no doc impact: README already documents the 320px phone behaviour (tasks 01–02); this task is test-only, no stale AGENTS.md facts. + +**Decisions** — git stash unavailable per harness rules, so pre-fix check used a file swap of `app.css` (restored immediately); live probe parameterised on selector since `#stop` lives in the Session panel, not the stage (`in_stage` asserted for `#start` only). + +**Next pending task** — `04_css_guard_and_docs` (`.agents/phases/todo/04_reachable_share_cta/04_css_guard_and_docs.md`). diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.md.status b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.validate b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.validate new file mode 100644 index 0000000..ddcb6de --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__03_reachable_cta_e2e.a1.validate @@ -0,0 +1,34 @@ +........................................................................ [ 26%] +.................ssss................................................... [ 53%] +........................................................................ [ 79%] +....................................................... [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 11 94% 108, 113, 117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 32 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:281: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:321: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:364: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:446: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +267 passed, 4 skipped, 1 warning in 69.49s (0:01:09) +All checks passed! +29 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.err b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.md b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.md new file mode 100644 index 0000000..9b67ccb --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.md @@ -0,0 +1,13 @@ +Everything is verified and green. Final report: + +**Phase 04, Task 04 — complete. All gates green.** + +- **`TestStylesheet::test_the_share_stage_is_one_cell`** — asserts `.stage` declares `grid-template: 1fr / 1fr` (or explicit rows/columns of 1fr) *and* `.stage > *` carries `grid-area: 1 / 1`; docstring records the 300×150 idle-video / 151px-stack / `overflow: hidden` clip reason. +- **`TestStylesheet::test_the_stage_has_a_floor_and_a_ceiling`** — `.stage` must keep `min-height` and `max-height`; any rule combining `overflow: hidden` with `aspect-ratio` needs `min-height` in the same block (the general statement of the bug). +- **`test_hidden_always_means_gone`** — `.placeholder` added to the `[hidden]`-toggled component list, with the inline comment corrected so it stays accurate. +- **Revert verification (by hand)**: deleting the grid lines fails the one-cell guard; deleting `min-height: 264px` fails the floor check; deleting both `max-height` lines fails the ceiling check. CSS restored; all 5 `TestStylesheet` tests green. +- **README.md L10** — added the `/share` alias (served by `app.py`, asserted by `test_pages_render`, previously undocumented). The narrow-screen user-facing line (L39) was already present from an earlier task; full-file consistency pass found no other gaps (ports, config defaults, key tables, kiosk commands all verified against code/docs). +- **AGENTS.md L44** — fixed the one stale fact: "Two pages" → "Three pages and one WebSocket" (the index page exists and is tested). No narrative added; "coverage ~95%" re-measured at 95%, still accurate. +- **PLAN.md** — untouched, but L204's "per-leg `maxBitrate` cap (default 12 Mbit/s)" is contradicted by reality: phase 02 raised the default to 25 Mbit/s (`config.py` L15, README L72). Reported here for the user to amend; I did not edit it. +- **Gates**: `uv run ruff check . && uv run ruff format --check .` clean · `uv run pyright` 0 errors · `uv run pytest` 269 passed / 4 skipped (realdisplay flag-gated) · `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` 3 passed / 1 skipped (host has no audio source — documented platform limitation). Phases 01–03 e2e suites (keyboard, sharpness, audio) all still green. +- **Next pending task**: none — task 04 was the last task of phase 04; the phase is ready for its final validation pass. diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.md.status b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.validate b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.validate new file mode 100644 index 0000000..e5588c8 --- /dev/null +++ b/.agents/reports/04_reachable_share_cta/04_reachable_share_cta__04_css_guard_and_docs.a1.validate @@ -0,0 +1,34 @@ +........................................................................ [ 26%] +.................ssss................................................... [ 52%] +........................................................................ [ 79%] +......................................................... [100%] +=============================== warnings summary =============================== +.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1 + /home/ducoterra/Projects/Personal/mirror/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +================================ tests coverage ================================ +_______________ coverage: platform linux, python 3.14.8-final-0 ________________ + +Name Stmts Miss Cover Missing +------------------------------------------------------ +src/mirror/__init__.py 3 0 100% +src/mirror/__main__.py 72 3 96% 129, 145, 156 +src/mirror/access.py 60 0 100% +src/mirror/app.py 185 12 94% 108, 113, 116-117, 182-183, 212-216, 245 +src/mirror/config.py 68 5 93% 110-111, 118-120 +src/mirror/debug.py 15 10 33% 19-28 +src/mirror/protocol.py 59 0 100% +src/mirror/registry.py 191 3 98% 166, 180, 355 +------------------------------------------------------ +TOTAL 653 33 95% +=========================== short test summary info ============================ +SKIPPED [1] tests/e2e/test_x11_capture.py:281: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:321: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:364: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +SKIPPED [1] tests/e2e/test_x11_capture.py:446: set MIRROR_REALDISPLAY=1 to exercise the real X11 capture backend +269 passed, 4 skipped, 1 warning in 68.64s (0:01:08) +All checks passed! +29 files already formatted +0 errors, 0 warnings, 0 informations diff --git a/AGENTS.md b/AGENTS.md index 9be6342..f964470 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,7 +41,7 @@ is a roster and a signaling relay. Linux is the reference platform. | Rule | Why it exists | |---|---| | **No external assets.** No CDN, remote font, remote icon, `@import`, or analytics | LAN/offline correctness. `tests/test_app.py::TestClientScripts::test_nothing_is_loaded_from_a_cdn` fails the build if you add one | -| **No framework, no bundler** in `src/mirror/static/` | Two pages and one WebSocket. `node --check` is the entire frontend toolchain | +| **No framework, no bundler** in `src/mirror/static/` | Three pages and one WebSocket. `node --check` is the entire frontend toolchain | | **Media never passes through the server** | The server relays JSON only. Anything that puts frames in the Python process violates A3 | | **`Registry` stays pure** — no sockets, no FastAPI, no `await` | It returns `list[Envelope]`. Transport lives in `Hub`. This is why the policy has 100%-ish unit coverage | | **camelCase on the wire, snake_case in Python**, via `alias_generator=to_camel` | A silently-dropped `displaySurface` was a real bug we shipped. Any new multi-word field needs an alias test | diff --git a/README.md b/README.md index 2714527..a0acb7c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ your network: | Page | URL | What it does | | ------------ | ------------------ | ----------------------------------------------------------------------- | -| **Share** | `/mirror` | Picks a window, screen, or tab via the OS-native picker and mirrors it. | +| **Share** | `/mirror` (also `/share`) | Picks a window, screen, or tab via the OS-native picker and mirrors it. | | **Watch** | `/watch` | Shows every screen currently being mirrored; click a tile to take the stage. | | **Index** | `/` | Signpost + live count of mirrors. | @@ -36,6 +36,10 @@ Linux is the primary target. On Linux the native picker is X11/Wayland's own (Chromium uses `xdg-desktop-portal` on Wayland/GNOME/KDE, and its built-in X11 picker on Xorg), so nothing has to be installed on the client side. +The share page works on a phone as well as a desktop: `/mirror` is usable +down to a 320 px-wide viewport, and the preview keeps its shape but never +shrinks below the controls it holds, so the Share button is always on screen. + The target *client* setup for an always-on display is Fedora Silverblue running GNOME Kiosk — see [`docs/kiosk-silverblue.md`](docs/kiosk-silverblue.md). diff --git a/docs/screenshots/phase-04/idle-1280x800.png b/docs/screenshots/phase-04/idle-1280x800.png new file mode 100644 index 0000000..2446e48 Binary files /dev/null and b/docs/screenshots/phase-04/idle-1280x800.png differ diff --git a/docs/screenshots/phase-04/idle-390x844.png b/docs/screenshots/phase-04/idle-390x844.png new file mode 100644 index 0000000..ce0d477 Binary files /dev/null and b/docs/screenshots/phase-04/idle-390x844.png differ diff --git a/docs/screenshots/phase-04/live-1280x800.png b/docs/screenshots/phase-04/live-1280x800.png new file mode 100644 index 0000000..e7a5970 Binary files /dev/null and b/docs/screenshots/phase-04/live-1280x800.png differ diff --git a/src/mirror/static/app.css b/src/mirror/static/app.css index 19ba615..70b13d0 100644 --- a/src/mirror/static/app.css +++ b/src/mirror/static/app.css @@ -225,27 +225,88 @@ a { color: var(--accent); } align-items: start; } -@media (max-width: 900px) { - .share { grid-template-columns: 1fr; } -} - +/* The stage is ONE cell. Its children — the