Skip to content

[3301 by Claude Opus 5.5] Make existing global search accessible by touch from the ProPR mobile menu - #3302

Merged
integry merged 3 commits into
mainfrom
3301/claude-opus-5-5-make-existing-global-sear-20261020-2202-hcd
Oct 21, 2026
Merged

integry merged 3 commits into
mainfrom
3301/claude-opus-5-5-make-existing-global-sear-20261020-2202-hcd

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #3301

Branch: 3301/claude-opus-5-5-make-existing-global-sear-20261020-2202-hcd
Commits: ✅ Changes committed (e7a4b36)


AI Processing Completed

Execution Details:

Summary:
On phones, a touch-only user can now open global search: the ProPR menu has a Search button in its header, next to Close. Tapping it closes the menu and opens the existing search sheet with its input focused.

How it works

  • The button goes through the same requestSearch path that Ctrl/Cmd+K and the native Search command already use (GlobalHeader.tsx). It doesn't fake a keypress, need native IPC, add an endpoint or change permissions.
  • The ProPR menu closes before Search opens, so only one modal layer is ever up. Focus moves to the More tab first, so closing Search (with Close, Escape or the backdrop) puts focus back on More. Opening and closing repeatedly behaves the same each time.
  • The ProPR menu has no unsent work to lose. The Create sheet, which holds the quick to-do draft, is untouched.
  • The logo, title and all destinations stay as they were. Toolbar search, Ctrl/Cmd+K, the native Search command on narrow or zoomed windows, and the Tasks/Plans/Goals list searches are unchanged.

Why it's in the header: my first version put a search row at the top of the menu body. With 150% text at 320px that pushed the Workspace tiles below the fold, and the existing branding test failed. A 44px icon button beside Close keeps everything else where it was.

Code changes

  • MobileBottomSheet.tsx gained an optional headerAction slot.
  • The button lives in a new file, MobileMenuSearchButton.tsx.
  • MobileBottomNavigation.tsx was already at the lint limit (399 of 400 code lines, complexity 20 of 20). So I moved the existing profile row unchanged into MobileProfileRow.tsx, and made onSearch required (GlobalHeader is its only caller).

Tests

  • New browser test (e2e/mobile-menu-search.pw.ts): a touch-only phone at 320×640 and 390×844, no desktop adapter. It covers:
    • opening Search by tap, with the input focused and only one modal layer.
    • Close and the search input not being covered by anything.
    • long titles (about 170 and 500 characters) shown in full with metadata below, no horizontal overflow and no raw model ID.
    • closing and reopening, with focus returning to More.
    • tapping a result to open the right task, then Ctrl+K still working.
  • New unit tests: the menu hands off to Search with focus on More, and in a plain browser the header opens the search sheet from the menu without a desktop adapter.
  • Results: 31/31 Playwright tests pass across the new spec and the existing branding, native-zoom, row-layout, palette and list-search specs. All 1560 component unit tests pass, tsc --noEmit is clean, and eslint . reports no problems.

I looked at the 320 and 390 screenshots, including the enlarged-text menu: nothing is clipped or overflows. A representative set is saved for preview.

Detailed Logs:

  • Conversation: 16 messages
  • Session: 85c34577-d2bd-4742-a719-5f9c95993978

Log files stored at:

  • conversation: /usr/src/app/logs/executions/issue-3301-2026-10-20T22-17-16-711Z-conversation.json
  • output: /usr/src/app/logs/executions/issue-3301-2026-10-20T22-17-16-711Z-output.txt
Latest Conversation Messages
ASSISTANT: [content unavailable]

ASSISTANT: On phones, a touch-only user can now open global search: the ProPR menu has a Search button in its header, next to Close. Tapping it closes the menu and opens the existing search sheet with its input ...


This PR was created automatically by ProPR after processing issue #3301.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!


Visual preview

ProPR menu with Search at 320px

ProPR menu with Search at 320px

The ProPR mobile sheet keeps its logo, title and destinations, with a new Search button beside Close

Original unavailable: managed storage is unavailable or the upload has expired.

ProPR menu with Search at 390px

ProPR menu with Search at 390px

The same sheet at 390px

Original unavailable: managed storage is unavailable or the upload has expired.

Search opened by touch at 320px

Search opened by touch at 320px

After tapping Search the menu has closed and the global search sheet is open with its input focused

Original unavailable: managed storage is unavailable or the upload has expired.

Global search results at 390px

Global search results at 390px

Real results for 170- and 500-character titles, with metadata below and no horizontal overflow

Original unavailable: managed storage is unavailable or the upload has expired.

ProPR menu, enlarged text, 320px

ProPR menu, enlarged text, 320px

With 150% text, Search stays in the header and the Workspace tiles stay above the fold

Original unavailable: managed storage is unavailable or the upload has expired.

…nu has a Search button in its header, next to Close. Tapping it closes the menu and opens the existing search sheet with its input focused.

How it works
- The button goes through the same `requestSearch` path that Ctrl/Cmd+K and the native Search command already use (`GlobalHeader.tsx`). It doesn't fake a keypress, need native IPC, add an endpoint or change permissions.
- The ProPR menu closes before Search opens, so only one modal layer is ever up. Focus moves to the More tab first, so closing Search (with Close, Escape or the backdrop) puts focus back on More. Opening and closing repeatedly behaves the same each time.
- The ProPR menu has no unsent work to lose. The Create sheet, which holds the quick to-do draft, is untouched.
- The logo, title and all destinations stay as they were. Toolbar search, Ctrl/Cmd+K, the native Search command on narrow or zoomed windows, and the Tasks/Plans/Goals list searches are unchanged.

Why it's in the header: my first version put a search row at the top of the menu body. With 150% text at 320px that pushed the Workspace tiles below the fold, and the existing branding test failed. A 44px icon button beside Close keeps everything else where it was.

Code changes
- `MobileBottomSheet.tsx` gained an optional `headerAction` slot.
- The button lives in a new file, `MobileMenuSearchButton.tsx`.
- `MobileBottomNavigation.tsx` was already at the lint limit (399 of 400 code lines, complexity 20 of 20). So I moved the existing profile row unchanged into `MobileProfileRow.tsx`, and made `onSearch` required (GlobalHeader is its only caller).

Tests
- New browser test (`e2e/mobile-menu-search.pw.ts`): a touch-only phone at 320×640 and 390×844, no desktop adapter. It covers:
  - opening Search by tap, with the input focused and only one modal layer.
  - Close and the search input not being covered by anything.
  - long titles (about 170 and 500 characters) shown in full with metadata below, no horizontal overflow and no raw model ID.
  - closing and reopening, with focus returning to More.
  - tapping a result to open the right task, then Ctrl+K still working.
- New unit tests: the menu hands off to Search with focus on More, and in a plain browser the header opens the search sheet from the menu without a desktop adapter.
- Results: 31/31 Playwright tests pass across the new spec and the existing branding, native-zoom, row-layout, palette and list-search specs. All 1560 component unit tests pass, `tsc --noEmit` is clean, and `eslint .` reports no problems.

I looked at the 320 and 390 screenshots, including the enlarged-text menu: nothing is clipped or overflows. A representative set is saved for preview.
@integry

integry commented Oct 20, 2026

Copy link
Copy Markdown
Owner

/review codex-astra

@propr-dev

propr-dev Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex-astra

Overall Evaluation

The PR adds a touch-accessible Search button to the ProPR menu and routes it through the existing global-search handler. The change is focused and appears merge-ready within the visible scope, conditional on pending checks completing successfully.

✅ Shared search behavior — The new button uses requestSearch, preserving the existing responsive routing without duplicating search logic.

✅ Stable focus return — The handler focuses More before requesting Search, giving the overlay a return target that survives the menu’s unmount.

✅ Relevant regression coverage — Added tests cover touch activation at both requested phone widths, dismissal, reopening, result navigation, and keyboard access.

This review is based on static inspection; no commands or tests were run. The supplied issue description is truncated, so acceptance criteria beyond its visible portion could not be assessed. The supplied diff is complete.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Clarify the handoff test

In propr-ui/src/components/MobileBottomNavigation.test.tsx, the new test records state.sheetOpen inside onSearch but never asserts it, while its title says the menu closes first. setOpenSheet(null) queues an update; it does not synchronously unmount the menu before onSearch() runs. Consider removing the unused snapshot and describing the committed modal replacement more precisely. This is optional test clarity: the browser test already checks that the resulting Search overlay is the only modal.

Score

The implementation satisfies the visible objective with a small integration change and relevant regression coverage. No code blockers were identified; the authoritative status reports no failed checks and six pending checks.

Score: 9/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 19s
  • Tokens: 52,896 (52,444 in / 452 out)
  • Cost: $0.44

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S1

🤖 Review by ProPR

@integry

integry commented Oct 20, 2026

Copy link
Copy Markdown
Owner

/review codex-astra

Your prior exact-head review reported that the supplied issue description was truncated. Please assess the full original acceptance below against head e7a4b36; these are existing requirements, not scope changes. Report blockers only if real; optional test-clarity suggestion S1 remains optional. Operator exact-head tests and CI are still running, so do not claim runtime or hardware qualification from static review.

  1. On ordinary web mobile layouts at 320px and 390px, a user can open ProPR, tap Search, and reach the existing global search overlay without keyboard input or a desktop adapter. Its input is visible and focused. Retain the ProPR title/logo and existing destinations.
  2. Close the originating sheet before presenting Search: no competing modal/inert layers, stolen input focus or blocked close control. Close/Escape and repeated open/close leave a usable, predictable focus target. Respect existing modal/draft boundaries; never discard unsent work merely to open Search.
  3. Enter a query, render real result UI (including meaningful 170/500-character titles with metadata below), and navigate by touch to the intended result without overflow or clipped controls. Preserve current filtering, page-specific search, keyboard navigation and Ctrl/Cmd+K.
  4. Keep Linux native menu Search, enlarged/narrow native layouts and macOS command behavior intact. Use the existing shared search request path rather than synthesizing a keypress or depending on native IPC from the browser. No raw model IDs in visible UI.
  5. Add focused real-browser mobile regressions which open Search using only touch, verify input focus, result navigation, modal handoff, close/reopen and no horizontal overflow. Cover a browser without desktop adapters, plus relevant existing native/keyboard tests. Inspect representative phone screenshots; run affected tests, TypeScript and lint.

@propr-dev

propr-dev Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex-astra

Overall Evaluation

The PR adds a touch-accessible Search control to the ProPR menu and routes it through the existing global-search request handler. Against the complete acceptance criteria supplied here, no merge-blocking defect is demonstrated. It is merge-ready within the reviewed scope, conditional on outstanding validation completing successfully.

✅ Shared search path — The new action calls requestSearch, preserving existing responsive routing without synthesizing keyboard events or requiring a desktop adapter.

✅ Predictable focus handoff — openSearch schedules menu removal and parks focus on More before the shared handler records its return target. The existing overlay focuses its input after mounting.

✅ Focused browser coverage — The added tests exercise touch entry at 320px and 390px, long result rendering, task navigation, close/reopen, focus restoration, and horizontal overflow.

This is a static review of the supplied code. I ran no commands or tests and inspected no screenshots; runtime, screenshot, TypeScript, lint, and hardware qualification are not established here. The authoritative status reports six pending checks and no failures.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S2: 🟢 Clarify handoff test timing

In propr-ui/src/components/MobileBottomNavigation.test.tsx, the new test captures state.sheetOpen inside onSearch but never asserts it. React batches the state updates, so scheduling menu closure does not mean its DOM has already disappeared inside that callback. Optionally remove the unused capture and describe the test as verifying focus parking and menu removal after the event. The browser test separately checks the resulting single-modal state, so this is test clarity rather than a demonstrated behavior defect.

Score

The implementation is focused, preserves the existing search behavior, and adds relevant regression coverage. No actionable findings or current-head failures are established; pending validation remains a condition of merge readiness.

Score: 8/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 23s
  • Tokens: 53,413 (52,913 in / 500 out)
  • Cost: $0.44

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S2

🤖 Review by ProPR

@integry

integry commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Please fix this confirmed mobile acceptance blocker before merging. The touch entry itself works, but the actual Android keyboard exposes a viewport defect in Search. Keep the implementation on Claude through ProPR.

Reproduced at reviewed head e7a4b36fb21289f03905c77fcd8309ac3e59b9b3 using its production browser bundle on the owned Android emulator, with real Chrome/Gboard, no device-metrics or visualViewport mocks, and read-only fixture APIs (not a physical-phone or final published-artifact claim):

  1. In portrait, open ProPR and tap Search. The input is focused; open the real keyboard if Chrome requires an additional input tap.
  2. Enter a query with a roughly 500-character task result plus a short final result. With the keyboard open, layout height stays 747 CSS px but visual height shrinks to 421.09 px. The palette extends from y=54 to y=505.91, behind the keyboard.
  3. Swipe the results using trusted touch. The result list has clientHeight=371 and scrollHeight=371, so the browser pans its visual viewport instead of a properly constrained results pane. visualViewport.offsetTop becomes 301.82 while the IME remains shown and the input stays active. The Search input remains at layout y=14..46 and Close at y=8..52, both completely outside the visible viewport. Raw OS screenshots show the lower results/footer above Gboard while the search field and Close have disappeared above the Chrome content area.

The independent portrait diagnostic failed specifically on “Search input and Close moved outside actual visual viewport after trusted scrolling with IME still open.” There were zero API writes or page errors, and the emulator display, rotation, reverse mappings, tabs, and owned server were restored. An earlier diagnostic incorrectly expected mobile bottom navigation at landscape width; that was a harness error, not the product finding above. Ordinary landscape crosses the responsive breakpoint and must use the visible toolbar search, or verify a deliberate rotation handoff.

Expected: keep the search field and a usable dismissal path in the actual visible frame, constrain/scroll results above the keyboard where space permits, and react correctly to both visualViewport resize and offset/scroll changes. Inspect the shared existing viewport helpers; do not force results to fit by truncating titles, hiding metadata, removing controls, or relying on closing the keyboard as the only workaround. Preserve ordinary desktop and Linux native search, enlarged/narrow layouts, macOS command behavior, touch result navigation, filtering, focus return, draft boundaries, and all original #3301 criteria. No authentication, permission, endpoint, or provider changes are needed.

Add focused browser regressions that hold layout dimensions constant while the visual viewport shrinks/pans and prove the input/dismissal and scrollable results remain usable. Retain real touch entry and relevant keyboard/native regressions. The operator will repeat the actual Android IME check and run fresh independent tests and Astra review at the resulting head. The prior 107 unit and 96 affected browser passes, and static Astra review, retain their original old-head scopes; they do not override this actual-device finding.

The operator has stopped the old-head automatic merge/build chain before any merge or new-source dispatch. Leave Git finalization to ProPR and do not merge or publish from the implementation task.

@propr-dev

propr-dev Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit 744566b

Summary of Changes

I fixed the keyboard defect in the phone Search sheet. Its field and Close search now stay in the visible area while Gboard is open, and results scroll in their own pane above the keyboard, including when Chrome pans the view. I tested this in headless Chromium with a stand-in visualViewport, not on a real Android device, so your emulator/Gboard check is still needed.

Cause

  • The Search sheet didn't use the shared useVisualViewportFrame hook that the Create, Follow Up, Logs and task dialogs already use. It stayed at the top of the layout viewport while the visible area shrank and moved.
  • The results palette took its maximum height from the full 747px layout height. So the results list was never shorter than its content (that's the 371/371 you measured), and a swipe on it panned the whole view instead of scrolling the list.

Changes

  • MobileSearchOverlay.tsx: the sheet now calls useVisualViewportFrame(dialogRef). It follows both resize and scroll of the visual viewport, so a pan moves the sheet with it. When the keyboard closes, the sheet's normal layout comes back.
  • GlobalSearch.tsx:
    • The palette's height is also capped at the bottom of the visible area, minus the usual 16px gap, and it re-measures on visual-viewport resize and scroll.
    • Its compact mode uses the same visible bottom.
    • The results list gets overscroll-contain, so a swipe that reaches the end of the list doesn't spill into panning the page.
    • Without a keyboard the new cap is never the smallest limit, so desktop, Linux native search and zoomed layouts behave as before.

Nothing is truncated or hidden to make results fit: long titles and metadata stay whole and every control remains. Ordinary landscape crosses the md breakpoint, so it still uses the toolbar search; I didn't change that.

Tests

  • New spec e2e/mobile-search-keyboard-viewport.pw.ts, run at 412×747 (your 421.09px band and 301.82px pan) and at 320×640 with proportionally scaled bands. The layout size is held constant throughout. It checks that:
    • the field and Close search are inside the visible band, aren't clipped, and take a tap.
    • the palette ends above the keyboard and its list actually scrolls.
    • a real CDP touch swipe scrolls the results pane while the field keeps focus.
    • after a pan without a resize, the field, Close search and results are still usable.
    • tapping the Tasks filter keeps focus in the field.
    • Close returns focus to More and no viewport listeners are left behind.
    • reopening while panned places the sheet in the current band, dismissing the keyboard restores the layout, and tapping the long task result opens it.
  • I moved the shared phone/search setup out of mobile-menu-search.pw.ts into mobile-menu-search.fixture.ts; that spec's tests are unchanged.
  • Against the old source, the new spec fails on both phones with "palette ends above the keyboard". With only the sheet change reverted, it fails with the Search field outside the visible area after the pan, which is the symptom you reported.

Results

  • 64/64 browser tests passed across 13 specs: the new one, mobile menu search and branding, native-zoom, palette, row-layout and scrollbar search, list and task searches, and the four existing keyboard-viewport specs. After a final small tidy I re-ran 4 of those specs (9 tests, all passed).
  • 1791/1791 component and hook unit tests passed; tsc --noEmit and eslint . are clean.
  • Not run: WebKit, a physical phone, and a published build.

The previews show the 412px band before the fix (field and Close search gone, palette behind the keyboard) and after, with the keyboard open and with the view panned.

Visual preview

After: keyboard open

After: keyboard open

Search sheet framed to the 421px visible band above the keyboard: field and Close search at the top, results scroll in their own pane, footer above the keyboard.

Original unavailable: managed storage is unavailable or the upload has expired.

Before: keyboard open, view panned

Before: keyboard open, view panned

Previous head at 412×747 with a 421px visible band panned to offsetTop 301.82 (the Android/Gboard geometry): the search field and Close search are above the visible area and the palette runs behind the keyboard.

Original unavailable: managed storage is unavailable or the upload has expired.

After: keyboard open, view panned

After: keyboard open, view panned

Same band panned to offsetTop 301.82: the sheet follows the visible area, so the field, Close search and the scrollable results stay on screen.

Original unavailable: managed storage is unavailable or the upload has expired.


🤖 Implementation Details

  • Model: Claude Opus 5.5 (medium)
  • Requested By: @integry
  • Time: 20m 46s
  • Tokens: 3,537,368 (3,508,373 in / 28,995 out)
  • Cost: $1.78

Undo Changes • View Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 6303839954✓

…field and Close search now stay in the visible area while Gboard is open, and results scroll in their own pane above the keyboard, including when Chrome pans the view. I tested this in headless Chromium with a stand-in `visualViewport`, not on a real Android device, so your emulator/Gboard check is still needed.

I fixed the keyboard defect in the phone Search sheet. Its field and Close search now stay in the visible area while Gboard is open, and results scroll in their own pane above the keyboard, including when Chrome pans the view. I tested this in headless Chromium with a stand-in `visualViewport`, not on a real Android device, so your emulator/Gboard check is still needed.

**Cause**
- The Search sheet didn't use the shared `useVisualViewportFrame` hook that the Create, Follow Up, Logs and task dialogs already use. It stayed at the top of the layout viewport while the visible area shrank and moved.
- The results palette took its maximum height from the full 747px layout height. So the results list was never shorter than its content (that's the 371/371 you measured), and a swipe on it panned the whole view instead of scrolling the list.

**Changes**
- `MobileSearchOverlay.tsx`: the sheet now calls `useVisualViewportFrame(dialogRef)`. It follows both resize and scroll of the visual viewport, so a pan moves the sheet with it. When the keyboard closes, the sheet's normal layout comes back.
- `GlobalSearch.tsx`:
  - The palette's height is also capped at the bottom of the visible area, minus the usual 16px gap, and it re-measures on visual-viewport resize and scroll.
  - Its compact mode uses the same visible bottom.
  - The results list gets `overscroll-contain`, so a swipe that reaches the end of the list doesn't spill into panning the page.
  - Without a keyboard the new cap is never the smallest limit, so desktop, Linux native search and zoomed layouts behave as before.

Nothing is truncated or hidden to make results fit: long titles and metadata stay whole and every control remains. Ordinary landscape crosses the `md` breakpoint, so it still uses the toolbar search; I didn't change that.

**Tests**
- New spec `e2e/mobile-search-keyboard-viewport.pw.ts`, run at 412×747 (your 421.09px band and 301.82px pan) and at 320×640 with proportionally scaled bands. The layout size is held constant throughout. It checks that:
  - the field and Close search are inside the visible band, aren't clipped, and take a tap.
  - the palette ends above the keyboard and its list actually scrolls.
  - a real CDP touch swipe scrolls the results pane while the field keeps focus.
  - after a pan without a resize, the field, Close search and results are still usable.
  - tapping the Tasks filter keeps focus in the field.
  - Close returns focus to More and no viewport listeners are left behind.
  - reopening while panned places the sheet in the current band, dismissing the keyboard restores the layout, and tapping the long task result opens it.
- I moved the shared phone/search setup out of `mobile-menu-search.pw.ts` into `mobile-menu-search.fixture.ts`; that spec's tests are unchanged.
- Against the old source, the new spec fails on both phones with "palette ends above the keyboard". With only the sheet change reverted, it fails with the Search field outside the visible area after the pan, which is the symptom you reported.

**Results**
- 64/64 browser tests passed across 13 specs: the new one, mobile menu search and branding, native-zoom, palette, row-layout and scrollbar search, list and task searches, and the four existing keyboard-viewport specs. After a final small tidy I re-ran 4 of those specs (9 tests, all passed).
- 1791/1791 component and hook unit tests passed; `tsc --noEmit` and `eslint .` are clean.
- Not run: WebKit, a physical phone, and a published build.

The previews show the 412px band before the fix (field and Close search gone, palette behind the keyboard) and after, with the keyboard open and with the view panned.

PR: #3302
Comment by: @integry (ID: 6303839954)
Model: Claude Opus 5.5
@integry

integry commented Oct 20, 2026

Copy link
Copy Markdown
Owner

/review codex-astra

Please independently review the updated implementation and assess the full original acceptance below against head 744566b; these are existing requirements, not scope changes. Report blockers only if real; optional test-clarity suggestion S1 remains optional. Operator exact-head tests and CI are still running, so do not claim runtime or hardware qualification from static review.

  1. On ordinary web mobile layouts at 320px and 390px, a user can open ProPR, tap Search, and reach the existing global search overlay without keyboard input or a desktop adapter. Its input is visible and focused. Retain the ProPR title/logo and existing destinations.
  2. Close the originating sheet before presenting Search: no competing modal/inert layers, stolen input focus or blocked close control. Close/Escape and repeated open/close leave a usable, predictable focus target. Respect existing modal/draft boundaries; never discard unsent work merely to open Search.
  3. Enter a query, render real result UI (including meaningful 170/500-character titles with metadata below), and navigate by touch to the intended result without overflow or clipped controls. Preserve current filtering, page-specific search, keyboard navigation and Ctrl/Cmd+K.
  4. Keep Linux native menu Search, enlarged/narrow native layouts and macOS command behavior intact. Use the existing shared search request path rather than synthesizing a keypress or depending on native IPC from the browser. No raw model IDs in visible UI.
  5. Add focused real-browser mobile regressions which open Search using only touch, verify input focus, result navigation, modal handoff, close/reopen and no horizontal overflow. Cover a browser without desktop adapters, plus relevant existing native/keyboard tests. Inspect representative phone screenshots; run affected tests, TypeScript and lint.

Also assess the confirmed Android IME refinement: keep the focused search input and usable Close inside actual visualViewport bounds on resize AND scroll/pan, constrain and scroll results above the real keyboard where space permits, preserve full titles/metadata/filtering/focus and desktop/native behavior. Inspect the shared viewport helper and effect ordering. New mobile-search-keyboard-viewport tests use a simulated viewport and Chromium CDP; distinguish that from WebKit/actual Android qualification. Root is repeating the actual Android failure case before full suites; no merge/publication until independent tests and current CI pass. Prior-head static review does not qualify this head.

@propr-dev

propr-dev Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex-astra

Overall Evaluation

The implementation follows the requested approach, and I found no verified PR-introduced code blocker. Merge readiness remains conditional on exact-head validation, including the actual Android keyboard reproduction.

✅ Shared search entry — The mobile Search button calls the existing requestSearch path, retaining browser independence, native commands, and Ctrl/Cmd+K.

✅ Focused modal handoff — The handler schedules the ProPR sheet’s removal and parks focus on More before requesting Search. The batched transition replaces the menu with Search; it does not require synchronous DOM removal inside the handler.

✅ Keyboard-aware geometry — The overlay reuses the shared viewport helper, while the palette measures the visible lower boundary on resize and scroll. In the supplied opening path, the overlay registers its framing listener before typing opens the palette and registers its measurement listener. Results retain their full rendering inside a constrained scroll pane.

This is a static review of the supplied diff and context for 744566b8e418cf02e76a973534918848708d9cd6. I ran no commands or tests and inspected no screenshots. The new regressions cover touch entry, focus, navigation, repeated dismissal, filtering, and simulated keyboard geometry, but they do not establish actual Android or WebKit qualification. The supplied current status has six pending checks and no failures.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S3: 🟢 Clarify handoff test timing

In propr-ui/src/components/MobileBottomNavigation.test.tsx, the new test captures state.sheetOpen inside onSearch but never asserts it. Because React batches the state updates, that snapshot can still show the originating sheet. Remove the unused snapshot and describe the assertion as a committed modal replacement. This is optional test clarity: the browser test already checks that the menu disappears and only one modal remains.

Score

The code appears sound within scope, with focused regression coverage. Required screenshot inspection, affected tests, TypeScript/lint results, and actual Android confirmation are not established by this static review, leaving a material verification gap beyond merely pending CI.

Score: 7/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 24s
  • Tokens: 60,264 (59,681 in / 583 out)
  • Cost: $0.52

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S3

🤖 Review by ProPR

@integry

integry commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Please diagnose and address this remaining exact-head validation failure through Claude, before this PR merges. Preserve all original mobile Search and actual Android IME requirements. Do not merge or publish from the implementation task.

At head 744566b8e418cf02e76a973534918848708d9cd6, the operator independently passed TypeScript, whole-UI lint, 140 affected unit tests, and all 78 expanded Chromium browser cases with no retries/skips. The real Android Chrome/Gboard portrait reproduction now passes: results scroll in their own pane (270px client / 371px content / 100.73px scroll), while the focused Search field and 44px Close remain visible and the IME stays open. Landscape toolbar Search, deliberate keyboard dismissal retaining the query, touch result navigation and portrait return also passed. Five raw OS screenshots were directly inspected. These use the exact-head production browser bundle with fixture APIs, not final signed/native or physical-phone acceptance.

The independent expanded WebKit run completed 77/78 passes, zero retries/skips, with one failure in existing e2e/global-search-native-zoom.pw.ts: native Search opens a visible, usable search control at narrow size, line75 expect(opener).toBeFocused(). Escape and the queried-palette backdrop dismissal had already restored opener focus. The third, empty-query Search dismissal removed the Search sheet but left the original All Repos button inactive. The test then never reached its final Ctrl+K/result-navigation assertions. The original trace/error context and extracted final screenshot are retained; this is not being waived as a flake.

A possible test-readiness race needs investigation: for the third opening the spec calls await sendSearch(page) (a page.evaluate dispatch of the native command), immediately performs a coordinate page.mouse.click(...), then checks absence of Search and opener focus. Unlike its first opening, it never first waits for the newly opened sheet to be present and its Search field focused. React's asynchronous commit may not be complete when the coordinate press occurs. That is a hypothesis, not a proven product diagnosis. Do not conceal a real focus defect by adding a long timeout, forcing focus, removing the assertion, retrying, or skipping a case.

Operator diagnostic instrumentation preserved the original assertions and event order. One focused narrow-case sample passed on both immutable old and new production bundles; five additional old-build samples also passed. Five new-build diagnostic samples are being collected separately with focus/pointer/native-command event audit attachments. Passing samples do not erase the original full-run failure or prove the cause. The new Search viewport tests themselves passed in the WebKit run using the explicitly scoped desktop-WebKit trusted wheel/tap adaptation for unsupported Chromium CDP swipes; this failure is in the original, unchanged native-zoom spec, not that adaptation.

Please inspect the actual open/close and focus event ordering and the test precondition. If this is a test race, add explicit user-observable mounted/focused Search readiness before the empty backdrop click, retaining all existing focus, close, repeated-open, keyboard, route, and layout assertions. If the product loses focus after a genuinely mounted, focused sheet is dismissed, fix the real ownership/event-order defect without altering IME geometry or desktop/native behavior. Include focused evidence distinguishing these cases, and test the existing native-zoom cases plus affected touch/keyboard regressions. No cosmetic, authorization, provider or endpoint changes are requested.

The operator is holding integration and has not started the new full merge/build/publication chain. Astra's static no-blocker review and positive Android/Chromium results retain their original scopes; they do not override this remaining failed acceptance assertion. Leave deterministic Git finalization to ProPR; the operator will inspect the resulting head and obtain independent Astra verification and fresh appropriate checks.

@propr-dev

propr-dev Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit 5668a2c

Summary of Changes

The WebKit failure was a real focus bug in the product. The test's timing only exposed it. It's now fixed in GlobalHeader.tsx, the original spec waits for the sheet to be ready, and a new test reproduces the bug every run.

Cause. The sheet focuses its Search field in an effect that runs just after the sheet appears. closeMobileSearch moved focus back to the opener as soon as a close was requested. If the backdrop was pressed in the short gap between the sheet appearing and the field taking focus, this happened:

  1. The opener was focused (it already was).
  2. React ran the pending effect, which focused the Search field.
  3. React removed the sheet, so focus fell to body.

That matches the WebKit symptom: the sheet was gone and All Repos was "inactive". I reproduced it on demand in both Chromium and WebKit with an in-page probe that presses the backdrop the moment the sheet appears. The focus log showed exactly that order: opener → Search field → body. When the sheet was fully open and focused before the press, as in the spec's first two dismissals, focus came back correctly. So your hypothesis was right about the trigger, but the lost focus itself was a product bug, not just a test artefact.

Fix. closeMobileSearch now only closes the sheet. Focus goes back to the opener after the sheet has been removed (GlobalHeader.tsx:112). Nothing the sheet still has pending can take focus after that. A route change still leaves focus to the new page. The keyboard-geometry (IME) handling, the desktop toolbar path, native commands and the sheet itself are unchanged.

Test changes (e2e/global-search-native-zoom.pw.ts):

  • Readiness wait: before the third, empty-query backdrop click, the spec now waits until the Search field is visible and focused, as the first opening already did. All existing assertions are kept and in the same order. No timeouts, forced focus, retries or skips were added.
  • New test: "a backdrop press as the narrow Search sheet appears still returns focus". It presses the backdrop in exactly that gap on every run, checks that the field wasn't focused yet, and expects the sheet gone and the opener focused. It then reopens Search and closes it with Escape.
  • Negative check: against the original GlobalHeader.tsx, the new test fails in both Chromium and WebKit with the operator's error ("Expected: focused / Received: inactive"). With the fix it passes.

Results:

  • Unit tests, typecheck, lint: 112 GlobalHeader/GlobalSearch/MobileBottomNavigation/viewport unit tests pass; tsc and lint (--max-warnings 0) are clean.
  • Chromium: all 74 cases in 12 affected specs pass with no retries. They cover the native-zoom, global-search, mobile-menu-search and Search keyboard-viewport specs, the dialog and short-band keyboard-viewport specs, list-search-mobile, dashboard-responsive, and the task-list desktop and search specs.
  • WebKit native-zoom: 40/40 passed, which is all 4 cases repeated 10 times with no retries.
  • WebKit, wider set: 17 passed and 2 failed. Both failures are the mobile-search-keyboard-viewport cases, which stop at newCDPSession, which WebKit doesn't support. Your scoped desktop-WebKit wheel/tap adaptation isn't in this branch, so those two cases didn't run on WebKit here. They did pass in the Chromium run.

This was all headless browser testing with fixture APIs, not an Android/Gboard or signed native check. I didn't capture previews because the change isn't visible on screen; it's where focus ends up.


🤖 Implementation Details

  • Model: Claude Opus 5.5 (medium)
  • Requested By: @integry
  • Time: 15m 18s
  • Tokens: 2,471,528 (2,470,980 in / 548 out)
  • Cost: $0.87

Undo Changes • View Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 6304371085✓

… test's timing only exposed it. It's now fixed in `GlobalHeader.tsx`, the original spec waits for the sheet to be ready, and a new test reproduces the bug every run.

The WebKit failure was a real focus bug in the product. The test's timing only exposed it. It's now fixed in `GlobalHeader.tsx`, the original spec waits for the sheet to be ready, and a new test reproduces the bug every run.

**Cause.** The sheet focuses its Search field in an effect that runs just after the sheet appears. `closeMobileSearch` moved focus back to the opener as soon as a close was requested. If the backdrop was pressed in the short gap between the sheet appearing and the field taking focus, this happened:

1. The opener was focused (it already was).
2. React ran the pending effect, which focused the Search field.
3. React removed the sheet, so focus fell to `body`.

That matches the WebKit symptom: the sheet was gone and All Repos was "inactive". I reproduced it on demand in both Chromium and WebKit with an in-page probe that presses the backdrop the moment the sheet appears. The focus log showed exactly that order: opener → Search field → `body`. When the sheet was fully open and focused before the press, as in the spec's first two dismissals, focus came back correctly. So your hypothesis was right about the trigger, but the lost focus itself was a product bug, not just a test artefact.

**Fix.** `closeMobileSearch` now only closes the sheet. Focus goes back to the opener after the sheet has been removed (`GlobalHeader.tsx:112`). Nothing the sheet still has pending can take focus after that. A route change still leaves focus to the new page. The keyboard-geometry (IME) handling, the desktop toolbar path, native commands and the sheet itself are unchanged.

**Test changes** (`e2e/global-search-native-zoom.pw.ts`):
- **Readiness wait:** before the third, empty-query backdrop click, the spec now waits until the Search field is visible and focused, as the first opening already did. All existing assertions are kept and in the same order. No timeouts, forced focus, retries or skips were added.
- **New test:** "a backdrop press as the narrow Search sheet appears still returns focus". It presses the backdrop in exactly that gap on every run, checks that the field wasn't focused yet, and expects the sheet gone and the opener focused. It then reopens Search and closes it with Escape.
- **Negative check:** against the original `GlobalHeader.tsx`, the new test fails in both Chromium and WebKit with the operator's error ("Expected: focused / Received: inactive"). With the fix it passes.

**Results:**
- **Unit tests, typecheck, lint:** 112 GlobalHeader/GlobalSearch/MobileBottomNavigation/viewport unit tests pass; `tsc` and lint (`--max-warnings 0`) are clean.
- **Chromium:** all 74 cases in 12 affected specs pass with no retries. They cover the native-zoom, global-search, mobile-menu-search and Search keyboard-viewport specs, the dialog and short-band keyboard-viewport specs, list-search-mobile, dashboard-responsive, and the task-list desktop and search specs.
- **WebKit native-zoom:** 40/40 passed, which is all 4 cases repeated 10 times with no retries.
- **WebKit, wider set:** 17 passed and 2 failed. Both failures are the `mobile-search-keyboard-viewport` cases, which stop at `newCDPSession`, which WebKit doesn't support. Your scoped desktop-WebKit wheel/tap adaptation isn't in this branch, so those two cases didn't run on WebKit here. They did pass in the Chromium run.

This was all headless browser testing with fixture APIs, not an Android/Gboard or signed native check. I didn't capture previews because the change isn't visible on screen; it's where focus ends up.

PR: #3302
Comment by: @integry (ID: 6304371085)
Model: Claude Opus 5.5
@integry

integry commented Oct 21, 2026

Copy link
Copy Markdown
Owner

/review codex-astra

Please independently review the updated implementation and assess the full original acceptance below against head 5668a2c; these are existing requirements, not scope changes. Report blockers only if real; optional test-clarity suggestion S1 remains optional. Operator exact-head tests and CI are still running, so do not claim runtime or hardware qualification from static review.

  1. On ordinary web mobile layouts at 320px and 390px, a user can open ProPR, tap Search, and reach the existing global search overlay without keyboard input or a desktop adapter. Its input is visible and focused. Retain the ProPR title/logo and existing destinations.
  2. Close the originating sheet before presenting Search: no competing modal/inert layers, stolen input focus or blocked close control. Close/Escape and repeated open/close leave a usable, predictable focus target. Respect existing modal/draft boundaries; never discard unsent work merely to open Search.
  3. Enter a query, render real result UI (including meaningful 170/500-character titles with metadata below), and navigate by touch to the intended result without overflow or clipped controls. Preserve current filtering, page-specific search, keyboard navigation and Ctrl/Cmd+K.
  4. Keep Linux native menu Search, enlarged/narrow native layouts and macOS command behavior intact. Use the existing shared search request path rather than synthesizing a keypress or depending on native IPC from the browser. No raw model IDs in visible UI.
  5. Add focused real-browser mobile regressions which open Search using only touch, verify input focus, result navigation, modal handoff, close/reopen and no horizontal overflow. Cover a browser without desktop adapters, plus relevant existing native/keyboard tests. Inspect representative phone screenshots; run affected tests, TypeScript and lint.

Also assess the confirmed Android IME refinement: keep the focused search input and usable Close inside actual visualViewport bounds on resize AND scroll/pan, constrain and scroll results above the real keyboard where space permits, preserve full titles/metadata/filtering/focus and desktop/native behavior. Inspect the shared viewport helper and effect ordering. New mobile-search-keyboard-viewport tests use a simulated viewport and Chromium CDP; distinguish that from WebKit/actual Android qualification. Root is repeating the actual Android failure case before full suites; no merge/publication until independent tests and current CI pass. Prior-head static review does not qualify this head.

Also independently review the latest two-file focus correction relative to744566b: GlobalHeader restores the saved target in a layout effect after the sheet unmounts, rather than synchronously during the close request. Check early backdrop dismissal before passive input focus, fully opened dismissal, Escape, repeat reopen, route-change and breakpoint ownership. The existing native-zoom test adds visible/focused readiness before its empty backdrop press and retains all original assertions; an additional MutationObserver-triggered case deliberately presses before field focus and asserts the opener is restored, then verifies reopen/Escape. Synthetic early-dispatch coverage is not real trusted-touch or Android proof. Operator independently preserved the original77/78WebKit failure at744, and actual320/393 Android geometry/pan positives at744 retain that original scope. Fresh566 checks are starting. Claude reports deterministic old-source failure in both engines, plus positive new-head tests; verify code correctness rather than relying on reported scores. The two upstream WebKit viewport test errors are unsupported CDP transport, not passes; the operator will retain its previously qualified scoped wheel/tap adaptation for WebKit and real Android tests. No merge until exact-head independent static/unit/focus/device/full suites and current required CI qualify.

@propr-dev

propr-dev Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex-astra

Overall Evaluation

The implementation follows the intended approach, and I found no verified PR-introduced blocker in the supplied diff. Merge readiness remains unconfirmed pending the requested exact-head runtime qualification and remaining CI check.

✅ Shared Search entry — The mobile menu’s accessible 44px Search button invokes the existing requestSearch path, preserving the ProPR branding and destinations without synthesizing keyboard events or requiring a desktop adapter.

✅ Focus restoration after removal — GlobalHeader.tsx restores the saved target in a layout effect after Search unmounts. This addresses the early-dismissal ordering problem: pending input focus cannot subsequently target the removed sheet. The target is cleared after restoration and checked for connectivity, supporting ordinary dismissal, Escape, reopening, and route-change cleanup.

✅ Keyboard-aware result bounds — The overlay uses the shared viewport helper, while the palette measures the visible lower edge on both resize and scroll. For the normal initially empty Search flow, the overlay’s framing listener registers before the palette opens and registers its measurement listener. Results retain their existing renderer and gain contained scrolling.

The supplied code preserves keyboard/native request routing, existing result navigation and filtering, and page-specific search paths. The new menu action closes the originating navigation sheet without introducing draft-clearing operations. Breakpoint dismissal retains its existing ownership rule; restoring a connected target does not itself guarantee that target remains visible after widening.

This was a static review only: I ran no commands or tests and inspected no screenshots. The synthetic viewport/CDP tests and MutationObserver dismissal test provide targeted regression coverage, but do not establish actual Android IME, WebKit, or trusted-touch qualification. Prior-head device results do not qualify 5668a2c. The authoritative check summary reports 0 failed, 1 pending, 7 passed, and 13 neutral/skipped.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S4: 🟢 Clarify modal handoff assertion

In propr-ui/src/components/MobileBottomNavigation.test.tsx, the new test captures state.sheetOpen during onSearch but never asserts it. React batches the state updates, so “closing the sheet first” describes the resulting committed presentation rather than a guaranteed unmount before the callback runs. Remove the unused capture and clarify that distinction in the test wording. This is optional: the browser test already checks that the menu disappears and only one modal remains after the handoff.

Score

The implementation and focused regression coverage are sound on static inspection. The requested exact-head browser, device, screenshot, TypeScript, and lint qualification remains unverified here, which is a material gap beyond the single pending CI check.

Score: 7/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 30s
  • Tokens: 62,010 (61,323 in / 687 out)
  • Cost: $0.54

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S4

🤖 Review by ProPR

@integry
integry merged commit 1fa3154 into main Oct 21, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make existing global search accessible by touch from the ProPR mobile menu

1 participant