Repository navigation
fix(mobile): stop the composer clipping the first draft line and cap the session header - #6522
Merged
iscekic merged 6 commits intoSep 25, 2026
Conversation
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (19 files)
Previous Review Summary (commit 37e8396)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 37e8396)Status: 2 Issues Found | Recommendation: Address before merge Executive SummaryThe line-snap fix is sound for the common case, but the Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (21 files)
Notes
Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0 Review guidance: REVIEW.md from base branch |
iscekic
marked this pull request as draft
September 22, 2026 07:22
…the session header #6522
iscekic
force-pushed
the
kwf/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5
branch
from
September 23, 2026 07:31
3e1dee0 to
6ceeb85
Compare
…he-message-input-0cd6e-41e5
# Conflicts: # apps/mobile/src/components/agents/session-detail-content.test.ts
# Conflicts: # apps/mobile/plugins/branded-splash.test.ts # apps/mobile/src/components/agents/new-session-prompt.tsx
The cap snap floored at the caller's one-line minimum (requested line-height + padding), but the snap measures the pitch the native input really renders (18.67dp against a requested 20dp on Android). A remaining-space cap that landed on that minimum therefore published the unaligned minimum, so a two-line draft still scrolled by 17.33 - a partial line - and clipped the first visible line, exactly the defect the snap exists to fix. Express the floor in whole native lines and clamp it to the cap, so the snapped height is always a whole number of native lines and never exceeds the space the composer has. Also correct the helper's doc comment: the caller passes the measured pitch, not the requested font-scaled line height.
iscekic
marked this pull request as ready for review
September 25, 2026 14:54
pandemicsyn
approved these changes
Sep 25, 2026
iscekic
deleted the
kwf/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5
branch
September 25, 2026 17:32
iscekic
added a commit
that referenced
this pull request
Sep 25, 2026
… cluster The merge of main exposed a real conflict of intent. This branch sized the header's trailing slot to its content (`ms-3 shrink-0`) so fixed-width controls stop painting past a clamped box. Main (#6513, pinned by #6522) relies on that slot's `max-w-[50%] min-w-0 shrink` cap so the session header's context pill truncates a long total cost inside the cap. Without the cap the pill's natural width pushes the title to zero and can paint past the screen edge. 21509eb rewrote main's assertion to match the uncapped slot, which hid that regression. Add `headerRightShrinks` to ScreenHeader: a cluster that shrinks on its own keeps the half-row cap, and fixed-width clusters stay content-sized. Both session headers (the loaded detail and the loading skeleton) opt in. Restore main's assertion unchanged.
iscekic
added a commit
that referenced
this pull request
Sep 26, 2026
…t, and sign-in screens (#6511) * fix: fix reviewer blocking findings from patch (kwf explorer-10-visual-defects-on-android-the-errored-questio-778f4-6366/patchf) * fix: kwf-fix-review-8c9c patch delivery * fix: fix the failed mobile-app verification gate (kwf kwf-fix-review-8c9c/gr1) * fix(mobile): size the session header's trailing cluster to its content The header clamped the trailing slot to half the row while its fixed-width children kept painting at full width, so the last control's glyphs ran past the right screen edge (session-compose-kbup). The cluster now sizes to its content and never shrinks; the flex-1 min-w-0 heading absorbs the squeeze and a window too narrow for both drops the actions to their own row. A variable-width headerRight still caps itself: Submit review and the Security Agent Save button carry 140 dp. * test(mobile): align the header tests with the content-sized cluster findInlineActionsWrapper no longer keys on the removed half-row cap; the stacked-row helper keys on the cluster's own class, and the cluster assertions check the wrapper's content instead of a superseded cap. * fix(mobile): stack header actions when the trailing cluster squeezes the title The stacking rule read the window width and the font scale only, so a wide trailing cluster stayed on the title's row. At 320 dp with a font scale of 2 the PR review header's Share, Submit review and Merge controls (236 dp of fixed-width buttons) left the title a few characters. `ScreenHeader` now reserves the width a caller declares for its `headerRight` cluster, and the decision subtracts the `ms-3` gap as well. The PR review screen declares the widths of the controls it actually renders, so a narrower cluster keeps the title beside it. * test(mobile): read the declared header width through findElement The test read `props` off the element it found, which the type checker sees as unknown, and it referenced a constant scoped to another suite. It now asserts the width through `findElement` and declares its own mergeable overview. * test(mobile): fix the declared-width stacking assertions The 236dp case at 390dp leaves the title 98dp, below its 120dp minimum, so the actions stack. The narrower-cluster test also inherited the previous test's 320dp / scale 2 window, so it now sets its own geometry. * fix(mobile): align the session header cluster test with the content-sized slot The screen-header change on this branch dropped the `max-w-[50%]` cap from the trailing slot, but session-detail-content.test.ts still asserted the capped wrapper and went red. Assert the new `ms-3 shrink-0` slot with the uncapped `min-w-0 shrink` cluster inside it. * fix(mobile): keep the half-row cap for the session header's shrinking cluster The merge of main exposed a real conflict of intent. This branch sized the header's trailing slot to its content (`ms-3 shrink-0`) so fixed-width controls stop painting past a clamped box. Main (#6513, pinned by #6522) relies on that slot's `max-w-[50%] min-w-0 shrink` cap so the session header's context pill truncates a long total cost inside the cap. Without the cap the pill's natural width pushes the title to zero and can paint past the screen edge. 21509eb rewrote main's assertion to match the uncapped slot, which hid that regression. Add `headerRightShrinks` to ScreenHeader: a cluster that shrinks on its own keeps the half-row cap, and fixed-width clusters stay content-sized. Both session headers (the loaded detail and the loading skeleton) opt in. Restore main's assertion unchanged. * chore(mobile): drop the node_modules symlink committed by mistake
This was referenced Sep 26, 2026
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.
Changelog for users
mainwhile this branch was open; this PR keeps the regression test that pins it).Changelog for maintainers
alignComposerInputHeightToLines). The line pitch comes from the real input's reported content height over the mirror's line count, because Android'sTextInputlays lines out at the font's own line box instead of the requestedlineHeight. Reviewuse-text-height.tsxfirst: the pitch estimate is the risky part. The snap's floor is expressed in whole native lines too, so a cap that lands on the composer's one-line minimum publishes a line-aligned height instead of republishing the unaligned minimum.useTextHeightnow also publishesmaxHeight; the composer input and the message input's scroll threshold read it. Callers that report no native content height keep the raw cap, so iOS is unchanged.min-w-0 shrinkinside the header's 50% slot, and the context pill shrinks and truncates its cost to one line, so the cluster stays inside the gutter.min-w-0 shrinkand bothnumberOfLines={1}slots were already on this branch's base (37e839621) and landed onmainas fix(mobile): keep session header trailing controls inside the viewport #6513 while this branch was open, so this file's merge delta is the explanatory comment only. This PR contributes the regression test that pins the capped cluster and the truncated cost.mobile-*flag names anddefault · not loaded) is refuted: that block is a__DEV__-only debug surface, and the shipped build does not render it. No product change.text-foregroundwhite; a mounted test now pins the token so it cannot silently drop to muted. No product change.colors.xmlholds nothing else) had already landed onmainwhile this branch was open, so the merge dropped the duplicate and this PR no longer carries it.@expo/router-server@57.0.9is patched so typed-route generation skips colocated test files, matching the Metro blocklist; otherwise(tabs)routes vanish from the generatedHrefunion and typecheck rejects valid navigations.E2E proof
[e5] e5 finding 5 (refuted: the title is foreground white in profile.png — 2298 title pixels above 200 luminance, max 255): the Profile screen's title paints in the same foreground token as every other… — jev read the digest: pass (confidence 1.00)
[e1] ux-check: On Android in dark mode, open a session detail with an associated PR and a long total cost: the PR badge and context pill stay fully inside the header's right gutter, no control is clipped… — android emulator-5554, head 71eb860; app Appearance verified Dark (e1-appearance-dark.txt: the only checked radio is
content-desc="Dark" checkable="true" checked="true"); the parked entry e1 was edited to add the Home->fixture navigation and re-ran end-to-end from Home:SCENE e1 OK(e1-full-scene.log) and after the pill tap the sheet digest shows the full value available,android.widget.TextView TOTAL COST tappable [55,1040][1025,1077]andandroid.widget.TextView $1,234,567.89 tappable [55,1086][1025,1142]; header geometry (e1-header.txt) shows nothing crossing the 1080px bezel, pill…[e10] ux-check: shipped build hides raw 'mobile-*' flags; dev build shows the feature-flags block only as the documented dev-only debug surface — Android emulator-5554: scripted run landed ('SCENE e10 OK', e10-scene.log) and its digest shows the dev build's Preferences Feature flags block ('TextView FEATURE FLAGS', 'TextView mobile-pr-review', 'TextView Enabled · default · not loaded', 'TextView mobile-quick-chat', 'TextView Off · default · not loaded'); the block is the documented dev-only surface ('Renders nothing when
__DEV__is false.' and 'if (!DEV || statuses.length === 0) {' in e10-flag-gate.log), so a non-DEV build renders none of these strings — that branch is unreachable on this dev-only device. Screenshots e10.png…[e10] ux-check: shipped build hides raw 'mobile-*' flags; dev build shows the feature-flags block only as the documented dev-only debug surface
[e5] e5 finding 5 (refuted: the title is foreground white in profile.png — 2298 title pixels above 200 luminance, max 255): the Profile screen's title paints in the same foreground token as every other…
[e6] ux-check: composer caps and scrolls without a cut first line or a shifted control row (android, emulator-5554) — Android dark mode (e6-dark-mode.txt: content-desc="Dark" checked="true"); ~9-line and ~11-line drafts both hold the input at height=327px = 120px 1-line min + 4 x the 52px per-line unit in e6-composer-geometry.log, and e6-input-9lines.txt / e6-input-12lines.txt give identical EditText [126,1922][800,2249], Mode: Code [28,1800][261,1873], Send message [926,2022][1052,2148] while the full draft text is still in the EditText: cap line-aligned, control row unmoved, no lost work; the glyph/cut appearance is judged by the visual reviewer on e6-input-12lines.png. No UX-DEFECT found.
[e4] e4 finding 4: tap the session title to open the rename dialog; with the name unchanged the primary Save is disabled and legible beside the crisp Cancel; $OUT/e4.png shows the dialog — SCENE e4 OK (e4-scene.log): dialog digest carries
android.widget.Button Cancel tappableandandroid.widget.Button Save [833,550][979,665]with no tappable marker, and the live tree (e4-dispatch.log) showsButton "Save" [disabled]; legibility (ink on the muted fill) is the visual reviewer's call on e4.png.E2E proof — log excerpts
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5/e2e-mobile-app/e1-full-scene.log/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5/e2e-mobile-app/e1-appearance-dark.txt/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5/e2e-mobile-app/e1-header.txt/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5/e2e-mobile-app/scripted-e1.logOwner request
[e8] ux-check: Rename session dialog, value unchanged, in dark mode — Android in dark mode (app Appearance = Dark: e8-appearance.txt shows the RadioButton immediately before the 'Dark' label with checked=true): the dialog opened with the value unchanged ('android.widget.EditText text=Greeting') and Save disabled ('android.widget.Button text= enabled=false') beside an enabled Cancel (enabled=true) per e8.txt, so Save reads as disabled and Cancel is actionable; label legibility against its fill (olive-on-olive) is the visual reviewer's call on e8.png; UX-DEFECT: none.
[e9] ux-check: Open Profile — the 'Profile' title renders in the same white foreground as other large headings, not muted gray, in both day and night mode — Profile reached in the app's Light (e9-day.png/e9-day.txt) and Dark (e9-night.png/e9-night.txt) appearance, both dumps carrying the header node
text="Profile" ... heading="true"; the foreground-white claim is a visual judgement owned by the visual reviewer on those stills. Device system uimode was restored to day and the app appearance left at Dark.[e3] e3 finding 3: header right cluster inside the gutter, no Copy link control — Android, session-open (dark): the scene log line 'android.widget.Button Context 10,943 of 1,310,720 tokens, 1% used, cost 0.39 cents. Tap to view context details. tappable [741,188][1043,303]' shows the context pill painted inside the 1080px screen with no 'Copy link' node anywhere in the digest (e3-scene.log, SCENE e3 OK), and the still e3.png carries the header's right edge for the visual reviewer; UX-DEFECT: none.
[e9] ux-check: Open Profile — the 'Profile' title renders in the same white foreground as other large headings, not muted gray, in both day and night mode
[e7] ux-check: header right cluster (PR badge + context pill) stays inside the gutter with a long total cost — Fixture: db.sh set total_cost_microdollars=1234567890000 on ses_f39a51c50ff63SKOB1TkZDfUg and dev:seed app:session-pr-link linked PR #1; app Appearance set to Dark via the Preferences radio. SCENE e7 OK; header digest shows 'open pull request #1' [541,223][646,270], the context pill [665,188][1043,303] and the cost '$1,234,567.89' [833,227][1013,264] — all right edges inside the 1045 gutter/screen; the context sheet digest shows 'TOTAL COST' -> '$1,234,567.89' (e7-context-sheet.txt), so the full value is available; no UX-DEFECT observed in behaviour.
[e1] ux-check: On Android in dark mode, open the Rename session dialog with the value unchanged: the Save label is legible against its fill (not olive-on-olive) and reads as disabled, with Cancel equally… — Android emulator-5554 in dark mode (app Appearance = Dark: e1-appearance-dark.xml carries
content-desc="Dark" checkable="true" checked="true") at state session-open (STATE HIT session-open, e1-state-session-open.log); the parked replay landedSCENE e1 OK(e1-scene.log) and opened the dialog with the value unchanged (e1-dialog.xmltext="Greeting" content-desc="Session name"), Save non-actionable (content-desc="Save" checkable="false" checked="false" clickable="true" enabled="false") beside an actionable Cancel (`content-desc="Cancel" checkable="false" checked="false" clickable="true"…[p4] ux-check: On Android in dark mode, open a session and type a 5+ line draft so the composer caps and scrolls: the top visible line of the draft shows its full glyph height (no line cut against the… — android/emulator-5604; state session-open restored (STATE HIT) and the parked replay ran 'SCENE p4 OK' (p4-run.log) after a cold restart cleared a prior scene's draft: the capped input 'android.widget.EditText Please add a health check endpoint called /API/health that returns the app version and how long the server has been up. Keep it small and add a test. Also add a short README note describing the endpoint. tappable [126,1941][800,2249]' (p4-run.log) is bottom-anchored beside the control row 'android.widget.Button Mode: Code tappable [28,1819][261,1892]' (p4-run.log), and that row stays at…
[e1] ux-check: On Android in dark mode, open a session detail with an associated PR and a long total cost: the PR badge and context pill stay fully inside the header's right gutter, no control is clipped… — android emulator-5554, head 71eb860; app Appearance verified Dark (e1-appearance-dark.txt: the only checked radio is
content-desc="Dark" checkable="true" checked="true"); the parked entry e1 was edited to add the Home->fixture navigation and re-ran end-to-end from Home:SCENE e1 OK(e1-full-scene.log) and after the pill tap the sheet digest shows the full value available,android.widget.TextView TOTAL COST tappable [55,1040][1025,1077]andandroid.widget.TextView $1,234,567.89 tappable [55,1086][1025,1142]; header geometry (e1-header.txt) shows nothing crossing the 1080px bezel, pill…Follow-ups (not changed here)
Owner manual verification
Owner verification is pending; these checks did not pass automatically.
Open findings (not fixed here)