fix(guide): rysuj ramkę celu pod kursorem, nie nad nim#74
Merged
Conversation
Czerwony prostokąt obrysowujący cel był adnotacją SVG doklejaną nad gotowym zrzutem — a kursor jest w tym zrzucie wypalony. PNG to płaskie piksele, więc warstwa dodana później przykrywa wszystko pod sobą, łącznie z kursorem, który czytelnik ma śledzić. Półprzezroczyste wypełnienie ramki kładło się na grocie strzałki. Ramka przenosi się więc tam, gdzie warstwy jeszcze istnieją: do żywej strony, przed migawkę. `cursor_effects.js` dostaje trwałe `frame()`/`clearFrame()` na z-index 2147483645 — pod kursorem (2147483647), tak jak filmowy błysk `highlight()`. Przeglądarka składa obie warstwy i dopiero wtedy spłaszcza je do PNG. `_framed_screenshot` obejmuje migawkę malowaniem i sprzątaniem; sprzątanie w `finally`, bo ramka żyje w stronie, nie w tym procesie — wyjątek pomiędzy zostawiłby ją na każdym kolejnym zrzucie, obrysowującą zły element. Dla `select:` maluje ją callback ujawnienia, bo tylko on zna pudełko kontrolki: zmierzone wcześniej należy do kontrolki zwiniętej. Adnotacja `kind="frame"` znika całkiem — razem z gałęzią w `_svg`, regułą `.frame` i polami x/y/w/h w `Annotation`. Zostawiona byłaby martwą ścieżką, która wygląda na działającą. Geometrię nadal wyznacza `target_shape`, więc polityka „co i gdzie obrysować" została w jednym miejscu. Zweryfikowane na zrzutach: kursor przecina dolną krawędź ramki i jest w całości widoczny, zarówno na kroku `click`, jak i `type` w popupie. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
… bez cichych domyślnych Trzy rzeczy, które wyszły przy przeglądzie własnej zmiany: 1. `cursor_effects.js` nie miał żadnego pokrycia w przeglądarce, mimo że testy overlay jeżdżą po prawdziwym Chromium. Zepsuty z-index przeszedłby przez wszystkie 184 testy — jedyną weryfikacją były oględziny zrzutu. Dochodzi pięć testów, w tym ten pilnujący jedynej własności, o którą w tej zmianie chodzi: ramka MUSI mieć niższy z-index niż kursor. Porównanie jest relacyjne, nie dosłowną stałą. Sprawdzone mutacją: podniesienie z-indeksu do maksimum i usunięcie rozsunięcia o pół obrysu — oba warianty czerwienią się. 2. `getattr(cap.recorder, "overlay", None)` maskowałoby zmianę nazwy atrybutu: ramka zniknęłaby ze wszystkich zrzutów, a testy zostałyby zielone. Każdy Recorder — prawdziwy i każdy dubler — ten atrybut ma, więc domyślna wartość była nieosiągalna. Zwykły dostęp, głośny AttributeError. 3. `frame()` przyjmowało `options`, których nikt nie podaje — cztery gałęzie bez pokrycia. Wartości wracają do stałych (tych samych, co dawna reguła `.frame`), a grubość obrysu i z-index dostają nazwane stałe z uzasadnieniem. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
CI złapało to, czego nie złapał mój self-review: przejrzałem tylko `tests/unit/`, a pięć testów integracyjnych asertowało na adnotacji `kind="frame"`, usuniętej razem z rysowaniem ramki w SVG. Te testy mierzą realną geometrię w prawdziwej przeglądarce — czy obrysowane pudełko to faktycznie pudełko kontrolki — więc skasowanie ich byłoby utratą pokrycia dokładnie tam, gdzie jest najcenniejsze. Zamiast tego `_frame_spy.py` nagrywa wywołania `Overlay.frame`, a asercje zachowują sens jeden do jednego: `_only_frame(painted)` w miejsce `_only(annotations, "frame")`. Szpieg woła oryginał dalej. Kilka z tych testów czyta potem piksele ze zrzutu, więc połknięcie wywołania po cichu zmieniłoby obraz, który badają. Pełny `pytest -m "not network"` lokalnie: 1604 passed, 1 skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Czerwony prostokąt obrysowujący cel kładł się na kursorze. Jego półprzezroczyste wypełnienie (
rgba(238,17,17,0.08)) przykrywało grot strzałki — czyli dokładnie to, co czytelnik ma śledzić.To nie był błąd rysowania, tylko konsekwencja warstw:
Kursor powstaje jeszcze w żywej stronie (
cursor.js,z-index: 2147483647) i popage.screenshot()jest już tylko grupą pikseli. PNG nie ma warstw, więc nic nie da się wsunąć pod jego fragment po fakcie.Rozwiązanie
Ramka przenosi się tam, gdzie warstwy jeszcze istnieją — do strony, przed migawkę:
cursor_effects.jsdostaje trwałeframe()/clearFrame()— obok istniejącegohighlight(), które jest filmowym błyskiem i samo znika po ~900 ms. Pudełko jest rozszerzane o połowę grubości obrysu, bo CSS rysujeborderdo środka, a SVGstrokecentruje na ścieżce: bez tego ramka byłaby o 2 px ciaśniejsza niż dotąd._framed_screenshotobejmuje migawkę malowaniem i sprzątaniem. Sprzątanie wfinally, bo ramka żyje w stronie, nie w tym procesie — wyjątek pomiędzy zostawiłby ją na każdym kolejnym zrzucie, obrysowującą zły element.Dla
select:maluje ją callback ujawnienia listy: tylko on zna pudełko kontrolki, bo zmierzone wcześniej należy do kontrolki zwiniętej, która dla page-enhanced selecta nigdy nie była na ekranie.Usunięty martwy kod
Adnotacja
kind="frame"znika całkiem — z gałęzią w_svg, regułą CSS.framei polamix/y/w/hwAnnotation. Zostawiona byłaby ścieżką, która wygląda na działającą i kusi do „przywrócenia". Geometrię nadal wyznaczatarget_shape, aFRAMED_ACTIONSnadal mówi które akcje obrysować — polityka została w jednym miejscu, zmienił się tylko malarz.Weryfikacja
Wygenerowany PDF, wycinek kroku
click— kursor przecina dolną krawędź ramki i jest w całości widoczny:To samo na kroku
typew wyskakującym oknie.tests/unit/guide/+tests/unit/overlay/: 184 passedruff checkz bramką C901 ≤ 10: czystoNowy
test_no_frame_is_ever_drawn_over_the_stilljest zamkiem na powrót: gdyby ktoś odtworzył rysowanie ramki w_svg, kursor znów wylądowałby pod czerwonym wypełnieniem, a test to złapie.🤖 Generated with Claude Code
https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5