Skip to content

feat(guide): popupy i zwijanie narracji w przewodniku PDF#73

Merged
mpasternak merged 4 commits into
mainfrom
feat/guide-popup-support
Jul 25, 2026
Merged

feat(guide): popupy i zwijanie narracji w przewodniku PDF#73
mpasternak merged 4 commits into
mainfrom
feat/guide-popup-support

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Problem

guidebot guide odrzucał każdy scenariusz, w którym jakikolwiek krok otwiera nowe okno:

BŁĄD: scenariusze z popupem nie są obsługiwane w `guide` v1 (krok otwiera nowe okno)

Dotyczyło to całej klasy scenariuszy logowania — a więc tych, dla których przewodnik krok-po-kroku ma największy sens.

Odmowa nie była kaprysem: pakiet nie miał żadnego cyklu życia okien. _Capture.page ustawiano raz z okna głównego i każde z pięciu wywołań _screenshot(cap.page, …) fotografowało właśnie je. Krok działający na popupie szukałby celu w oknie głównym i padał na niezgodności tożsamości — kilka kroków za późno, w niezrozumiałym miejscu.

Rozwiązanie

Zmienia się jedno pojęcie: _Capture.page znaczy teraz „okno aktywne", nie „okno główne". Pętla przechwytywania nie dowiaduje się o niczym — te same pięć wywołań fotografuje inną wartość.

Cykl życia sterowany sidecarem, nie heurystyką. compile już zamraża opens_popup: true, więc klik z tą flagą owija się w context.expect_page() i przełącza parę (page, recorder); closeWindow przełącza z powrotem. Ta komenda była dotąd wyłącznie narracyjna — nie miała czego zamykać — więc dostaje własny rodzaj strony.

Świadomie bez render/popup_detect.py i render/popup_crop.py (789 linii): to maszyneria wideo — wykrywanie momentu z kwantem ciszy, przejścia float/slide, kadrowanie klatek. PDF nie ma osi czasu.

Płótno w CSS, nie w obrazie. Popup otwiera się mniejszy niż okno główne, więc layout.py centruje go na płótnie o proporcjach okna głównego — bez przetwarzania obrazu i bez nowej zależności. Adnotacje przenoszą się z .shot do wewnętrznego .plate o rozmiarze zrzutu: przypięte do .shot rozjechałyby się o szerokość marginesu. Sam fakt „to popup" nie jest nigdzie flagą — różnica screenshot_size i canvas_size nią jest, więc dwa źródła prawdy nie mogą się rozjechać.

Weryfikacja

End-to-end na scenariuszu logowania do Onetu, dotąd odrzucanym:

[7/10] action      ← krok w wyskakującym oknie
[9/10] closeWindow
zbudowano przewodnik: s.pdf (8 stron)

Rozmiary zrzutów potwierdzają przełączenie okien: step-006.png ma 500×670 (popup), pozostałe 1376×800 (okno główne).

  • pytest -q -m "not network" (komenda z ci.yml): 1591 passed, 1 skipped
  • ruff check z bramką C901 ≤ 10: czysto
  • Limit 600 linii/plik: największy to replay.py — 534

Testy

Nowy tests/unit/guide/test_capture_popup.py (7 przypadków, bez przeglądarki) pinuje kolejność, bo to ona jest sednem: krok otwierający popup jest fotografowany w oknie, w którym czytelnik klika, a dopiero kolejne — w popupie. Odwrotnie i każdy przewodnik z logowaniem pokazuje obrazek złego okna w momencie akcji.

Poza tym: powrót na closeWindow, zerowanie śladu kursora przy zmianie okna (strzałka między oknami wskazywałaby współrzędne, które na tej stronie nic nie znaczą), brak okna mimo flagi → twardy GuideError z plik:linia, oraz --pause-on-error zatrzymujący się na oknie, w którym krok padł.

Dawne test_scan_raises_on_popup zostało odwrócone w test_scan_allows_a_click_that_opens_a_popup.

Spec: docs/superpowers/specs/2026-07-25-guide-popup-support-design.md

🤖 Generated with Claude Code

https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5

mpasternak and others added 4 commits July 25, 2026 03:09
Opisuje zamianę `_Capture.page` z „okna głównego" na „okno aktywne",
cykl życia sterowany zamrożoną flagą `opens_popup` zamiast heurystyki,
oraz letterboxing popupu w CSS zamiast przetwarzania obrazu.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
`guide` odrzucał każdy scenariusz z `opens_popup: true` — a więc całą klasę
scenariuszy logowania, dla których przewodnik krok-po-kroku ma największy sens.
Odmowa była uczciwa: pakiet nie miał żadnego cyklu życia okien, `_Capture.page`
było ustawiane raz z okna głównego, a krok działający na popupie szukałby celu
w złym oknie i padał kilka kroków później na niezgodności tożsamości.

Zmienia się jedno pojęcie: `_Capture.page` znaczy teraz „okno aktywne", nie
„okno główne". Pętla przechwytywania nie dowiaduje się o niczym — te same pięć
wywołań `_screenshot(cap.page, ...)` fotografuje inną wartość.

Cykl życia sterowany jest zamrożoną flagą, nie heurystyką: klik z `opens_popup`
owija się w `context.expect_page()` i przełącza parę `(page, recorder)`,
a `closeWindow` — dotąd komenda wyłącznie narracyjna, bo nie miała czego
zamykać — dostaje własny rodzaj strony i przełącza z powrotem. Świadomie bez
`render/popup_detect.py` i `popup_crop.py`: te 789 linii to maszyneria wideo,
a PDF nie ma osi czasu.

Popup jest mniejszy od okna głównego, więc `layout.py` centruje go na płótnie
o proporcjach okna głównego — w CSS, bez przetwarzania obrazu. Adnotacje
przenoszą się z `.shot` do wewnętrznego `.plate` o rozmiarze zrzutu: pinowane
do `.shot` rozjechałyby się o szerokość marginesu. Sam fakt „to popup" nie jest
nigdzie flagą — różnica `screenshot_size` i `canvas_size` nią jest.

Zweryfikowane end-to-end na scenariuszu logowania do Onetu: 8 stron, zrzut
popupu 500x670 obok stron 1376x800 okna głównego.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
`--pause-on-error` dostawał parametr `page` funkcji `capture_pages`, czyli
zawsze okno główne. Dopóki było jedno okno, była to jedyna możliwa odpowiedź.
Odkąd `_Capture.page` znaczy „okno aktywne", błąd w popupie zostawiałby
dewelopera przed oknem głównym, na którym nic złego nie widać.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
Krok `say:` między akcjami opisuje to, na co czytelnik patrzy — a dostawał
osobną kartkę z jednym zdaniem na środku. Teraz ląduje w panelu bocznym
poprzedniej strony ze zrzutem, pod poziomą kreską.

Zwijanie nie dotyczy slajdów (celowa plansza pełnoekranowa nie absorbuje ani
nie jest absorbowana) ani narracji, przed którą nie ma żadnego obrazka.
Pusta narracja zwija się w nic zamiast w samotną kreskę — przy okazji znikają
puste kartki po krokach narracyjnych bez tekstu.

`fold_narration` jest publiczne i wołane przez `run_guide`, nie przez
`render_html`. Zwijanie ukryte w rendererze zostawiało wywołującemu listę
sprzed zwinięcia jako jedyną miarę dokumentu — a `guide` zwraca jej długość
jako liczbę stron, więc CLI ogłaszało osiem stron pięciostronicowego PDF-a.
Jedna lista, liczona i drukowana, nie może się rozjechać.

Na scenariuszu logowania do Onetu: 8 stron -> 5, bez utraty jednego słowa
narracji.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEZwp8YivARFrZSjrSUnD5
@mpasternak mpasternak changed the title feat(guide): obsłuż popupy w przewodniku PDF zamiast je odrzucać feat(guide): popupy i zwijanie narracji w przewodniku PDF Jul 25, 2026
@mpasternak

Copy link
Copy Markdown
Member Author

Dodane: zwijanie narracji bez własnego kadru

Drugi, niezależny problem tej samej ścieżki: krok say: między akcjami dostawał osobną kartkę z jednym zdaniem na środku. Teraz ląduje w panelu bocznym poprzedniej strony ze zrzutem, pod <hr>.

Zwijanie nie dotyczy slajdów (celowa plansza pełnoekranowa ani nie absorbuje, ani nie jest absorbowana) i narracji, przed którą nie ma obrazka. Pusta narracja zwija się w nic zamiast w samotną kreskę — przy okazji znikają puste kartki po krokach narracyjnych bez tekstu.

fold_narration jest publiczne i wołane przez run_guide, nie przez render_html. Zwijanie ukryte w rendererze zostawiało wywołującemu listę sprzed zwinięcia jako jedyną miarę dokumentu — a guide zwraca jej długość jako liczbę stron, więc CLI ogłaszało osiem stron pięciostronicowego PDF-a. Złapane przy weryfikacji end-to-end i zamknięte testem test_fold_narration_is_what_the_document_is_counted_by.

Na scenariuszu logowania do Onetu: 8 stron → 5, bez utraty jednego słowa narracji.

1. slajd tytułowy
2. [zrzut onet.pl]  + „Pokażę, jak dojść do logowania…”
3. [zrzut kliknięcia] + „Okno logowania otwiera się w osobnym…”
4. [zrzut popupu]   + „Zamykamy okno logowania i wracamy…”
5. slajd końcowy

@mpasternak
mpasternak merged commit fcf3df8 into main Jul 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant