Skip to content

fix(mobile): stop the composer clipping the first draft line and cap the session header - #6522

Merged
iscekic merged 6 commits into
mainfrom
kwf/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5
Sep 25, 2026
Merged

iscekic merged 6 commits into
mainfrom
kwf/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5

Conversation

@iscekic

@iscekic iscekic commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • On Android, a long draft in the message input no longer loses the top half of its first visible line when the field caps and scrolls.
  • A long session total cost truncates inside the header's context pill instead of overflowing past the screen edge (fix(mobile): keep session header trailing controls inside the viewport #6513 already shipped this on main while this branch was open; this PR keeps the regression test that pins it).

Changelog for maintainers

  • Composer input cap now floors to a whole number of rendered lines (alignComposerInputHeightToLines). The line pitch comes from the real input's reported content height over the mirror's line count, because Android's TextInput lays lines out at the font's own line box instead of the requested lineHeight. Review use-text-height.tsx first: 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.
  • useTextHeight now also publishes maxHeight; 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.
  • The session header's right cluster is min-w-0 shrink inside 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 shrink and both numberOfLines={1} slots were already on this branch's base (37e839621) and landed on main as 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.
  • Finding 2 (Preferences showing raw mobile-* flag names and default · not loaded) is refuted: that block is a __DEV__-only debug surface, and the shipped build does not render it. No product change.
  • Finding 4 (olive-on-olive Save) was already fixed by 94bbafb: with the name unchanged the Save label is disabled and legible beside the crisp Cancel. No product change.
  • Finding 5 (muted Profile title) is refuted: the large route title paints text-foreground white; a mounted test now pins the token so it cannot silently drop to muted. No product change.
  • The branded-splash test repair (assert the splash entry is present rather than that colors.xml holds nothing else) had already landed on main while this branch was open, so the merge dropped the duplicate and this PR no longer carries it.
  • New dependency-policy exception, exact version only: @expo/router-server@57.0.9 is patched so typed-route generation skips colocated test files, matching the Metro blocklist; otherwise (tabs) routes vanish from the generated Href union and typecheck rejects valid navigations.

E2E proof

e3 finding 3: a session's header right cluster (PR badge + context pill) stays inside the header gutter and no Copy link control is painted in the header (it moved into the context sheet in… — p5-sheet.png

e4 finding 4 (already fixed by 94bbafb5c): tap the session title to open the rename dialog; with the name unchanged the primary Save is disabled and legible (ink on the muted fill) beside the crisp… — p3.png

[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)

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… — e5.png

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… — p4-dark.png

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… — e4-header.png

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… — e1-detail.png

[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] and android.widget.TextView $1,234,567.89 tappable [55,1086][1025,1142]; header geometry (e1-header.txt) shows nothing crossing the 1080px bezel, pill…

[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… — e2e-mobile-app/e1.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 — 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 — prior/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

[e10] ux-check: shipped build hides raw 'mobile-*' flags; dev build shows the feature-flags block only as the documented dev-only debug surface — prior/e10-profile.png

[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…

[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… — scripted-shard5/e5.png

[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.

[e6] ux-check: composer caps and scrolls without a cut first line or a shifted control row (android, emulator-5554) — prior/e6-input-12lines.png

[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 tappable and android.widget.Button Save [833,550][979,665] with no tappable marker, and the live tree (e4-dispatch.log) shows Button "Save" [disabled]; legibility (ink on the muted fill) is the visual reviewer's call on e4.png.

[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 — scripted-shard4/e4.png

E2E proof — log excerpts

[e1] ux-check: On Android in dark mode, open a session detail with an associated -> pass :: 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]` and `android.widget.TextView $1,234,567.89 tappable [55,1086][1025,1142]`; header geometry (e1-header.txt) shows nothing crossing the 1080px bezel, pill `content-desc="Context 18,074 of 1,310,720 tokens, 1% used, cost 1,234,567 dollars 89 cents. Tap to v
[e1] e1 finding 1 (composer first line): open a session and type the six-line dr -> pass :: jev read the digest: pass (confidence 1.00)
/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
android.widget.TextView https://app.kilo.ai/cloud/sessions/ses_f385e02f5ffeDBLVYOE26ehfFO?at=msg_0c7a1fd67001tckswd04Cf6BSm tappable [55,641][1025,752]
android.widget.TextView RUN ON tappable [55,789][1025,826]
android.widget.TextView kwf-explorer-5-visual-defects-on-android-in-the-message-i-0cd6e · explorer-5-visual-defects-on-android-in-the-message-input-0cd6e- tappable [55,835][1025,1003]
android.widget.TextView TOTAL COST tappable [55,1040][1025,1077]
android.widget.TextView $1,234,567.89 tappable [55,1086][1025,1142]
android.widget.TextView Usage reflects the latest completed assistant response. tappable [55,1178][1025,1215]
android.widget.TextView Token usage tappable [55,1289][1025,1335]
android.widget.TextView Input tappable [55,1372][131,1418]
android.widget.TextView 16,024 tappable [922,1372][1024,1418]
android.widget.TextView Output tappable [55,1446][155,1492]
android.widget.TextView 2 tappable [1005,1446][1024,1492]
android.widget.TextView Reasoning tappable [55,1519][211,1565]
android.widget.TextView 0 tappable [1005,1519][1024,1565]
android.widget.TextView Cache read tappable [55,1593][222,1639]
android.widget.TextView 2,048 tappable [941,1593][1024,1639]
android.widget.TextView Cache write tappable [55,1666][229,1712]
android.widget.TextView 0 tappable [1005,1666][1024,1712]
android.widget.TextView Total tappable [55,1740][129,1786]
android.widget.TextView 18,074 tappable [922,1740][1024,1786]
android.widget.TextView CACHE RATE tappable [55,1813][1025,1850]
android.widget.TextView 11.3% tappable [55,1859][1025,1915]
android.widget.TextView Models (2) tappable [55,1989][1025,2035]
android.widget.Button DeepSeek V4 Flash 0731, Kilo Gateway, 1 step, $0.0071 tappable [58,2066][1022,2209]
android.widget.TextView DeepSeek V4 Flash 0731 tappable [146,2094][856,2140]
/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
<redacted>
<redacted>
                                        </com.horcrux.svg.GroupView>
                                      </com.horcrux.svg.SvgView>
                                    </android.view.View>
                                  </android.view.ViewGroup>
                                </android.view.ViewGroup>
                              </android.view.ViewGroup>
                            </android.widget.ScrollView>
                          </android.view.ViewGroup>
                        </android.view.ViewGroup>
                      </android.view.ViewGroup>
<redacted>
                    </android.view.ViewGroup>
                  </android.view.ViewGroup>
                </android.view.ViewGroup>
              </android.widget.FrameLayout>
            </android.widget.FrameLayout>
          </android.widget.FrameLayout>
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.widget.LinearLayout>
  </android.widget.FrameLayout>
</hierarchy>
/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
                                          </com.horcrux.svg.SvgView>
                                        </android.widget.Button>
                                      </android.view.ViewGroup>
                                    </android.view.ViewGroup>
                                  </android.view.ViewGroup>
<redacted>
                                </android.view.ViewGroup>
                              </android.view.ViewGroup>
                            </android.widget.ScrollView>
                          </android.view.ViewGroup>
                        </android.view.ViewGroup>
                      </android.view.ViewGroup>
<redacted>
                    </android.view.ViewGroup>
                  </android.view.ViewGroup>
                </android.view.ViewGroup>
              </android.widget.FrameLayout>
            </android.widget.FrameLayout>
          </android.widget.FrameLayout>
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.widget.LinearLayout>
  </android.widget.FrameLayout>
</hierarchy>
/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.log
android.widget.TextView CLOUD tappable [87,558][468,595]
android.widget.TextView $1,234,567.89 · 1 HOUR AGO tappable [512,558][949,595]
android.widget.TextView UX-PR-COST-FIXTURE really quite a long session title to squeeze the header right cluster tappable [87,602][950,694]
android.widget.TextView kwf/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5 tappable [87,704][950,778]
android.widget.Button New coding task tappable [37,870][1043,986]
android.widget.TextView New coding task tappable [450,904][696,950]
android.widget.Button New task from a picture tappable [37,1004][1043,1120]
android.widget.TextView New task from a picture tappable [396,1038][749,1084]
android.widget.TextView EXPLORE tappable [36,1156][1044,1193]
android.widget.Button Code Reviewer, Automatic PR reviews tappable [37,1212][1043,1356]
android.widget.TextView Code Reviewer tappable [171,1240][953,1286]
android.widget.TextView Automatic PR reviews tappable [171,1290][953,1327]
android.widget.Button Security Agent, Find and remediate vulnerabilities tappable [37,1374][1043,1518]
android.widget.TextView Security Agent tappable [171,1402][953,1448]
android.widget.TextView Find and remediate vulnerabilities tappable [171,1452][953,1489]
android.widget.Button PR Review, Review pull requests on mobile tappable [37,1537][1043,1663]
android.widget.TextView PR Review tappable [171,1565][953,1611]
android.widget.TextView Review pull requests on mobile tappable [171,1615][953,1652]
android.view.View Home, tab, 1 of 3 tappable [0,2195][360,2337]
android.widget.TextView HOME tappable [13,2281][347,2320]
android.view.View Agents, tab, 2 of 3 tappable [360,2195][720,2337]
android.widget.TextView AGENTS tappable [373,2281][707,2320]
android.view.View Profile, tab, 3 of 3 tappable [720,2195][1080,2337]
Owner request

Surface: mobile-app

Explorer finding: 5 visual defects on android: In the message input, the first line of ; the flag names "mobile-pr-review"/"mobil; The header meta row overflows the right (+2 more)

The user-agent explorer found these 5 while using the app like a user. They are one kind of defect on one platform, so they are ONE item: the pull request must fix and prove every one of them.
One must-run scenario per finding: the plan needs 5 musts, one for each finding below.
The explorer never edits product code.

Evidence (from the device runs):

--- Finding 1 of 5: compose-filled: In the message input, the first line of the composed text is clipped by the top edge of the field, cutting off the upper half of the glyphs.
Flow: compose-filled
Found on revision: f2181ae
Repro:

  1. set this state first: org; seed kiloclaw:fake-org-instance bf89f948-5a6d-4df0-a61e-e402494fcbe1 1df30c13-e3be-4035-9ddf-fec0bbc5fac4; uimode night; battery 20 discharging; relaunch; key back; the device in dark mode
  2. open the app on emulator-5602
  3. reach compose-filled
  4. the capture shows the defect named below
    Observed: In the message input, the first line of the composed text is clipped by the top edge of the field, cutting off the upper half of the glyphs.
    Expected: the screen renders without this defect

--- Finding 2 of 5: script-probe: A user-facing Preferences screen shows raw developer strings in an otherwise Arabic UI: the flag names "mobile-pr-review"/"mobile-quick-chat" and the status "default · not loaded".
Flow: script-probe
Found on revision: f2181ae
Repro:

  1. set this state first: org; seed kiloclaw:fake-org-instance bf89f948-5a6d-4df0-a61e-e402494fcbe1 1df30c13-e3be-4035-9ddf-fec0bbc5fac4; uimode night; battery 20 discharging; relaunch; key back; the device in dark mode
  2. open the app on emulator-5602
  3. reach script-probe
  4. the capture shows the defect named below
    Observed: A user-facing Preferences screen shows raw developer strings in an otherwise Arabic UI: the flag names "mobile-pr-review"/"mobile-quick-chat" and the status "default · not loaded".
    Expected: the screen renders without this defect

--- Finding 3 of 5: session-detail: The header meta row overflows the right edge and the Copy link control is sliced by the screen bezel (same slice visible in session-compose-kb-down.png and session-compose-kb-up.png).
Flow: session-detail
Found on revision: f2181ae
Repro:

  1. set this state first: org; seed kiloclaw:fake-org-instance bf89f948-5a6d-4df0-a61e-e402494fcbe1 1df30c13-e3be-4035-9ddf-fec0bbc5fac4; uimode night; battery 20 discharging; relaunch; key back; the device in dark mode
  2. open the app on emulator-5602
  3. reach session-detail
  4. the capture shows the defect named below
    Observed: The header meta row overflows the right edge and the Copy link control is sliced by the screen bezel (same slice visible in session-compose-kb-down.png and session-compose-kb-up.png).
    Expected: the screen renders without this defect

--- Finding 4 of 5: session-permission: The dialog's primary action "Save" is drawn olive-on-olive and is effectively illegible, so it reads as disabled while "Cancel" is crisp white beside it.
Flow: session-permission
Found on revision: f2181ae
Repro:

  1. set this state first: org; seed kiloclaw:fake-org-instance bf89f948-5a6d-4df0-a61e-e402494fcbe1 1df30c13-e3be-4035-9ddf-fec0bbc5fac4; uimode night; battery 20 discharging; relaunch; key back; the device in dark mode
  2. open the app on emulator-5602
  3. reach session-permission
  4. the capture shows the defect named below
    Observed: The dialog's primary action "Save" is drawn olive-on-olive and is effectively illegible, so it reads as disabled while "Cancel" is crisp white beside it.
    Expected: the screen renders without this defect

--- Finding 5 of 5: The screen title "Profile" is rendered in muted gray while the same title on profile-arabic.png and every other heading is white, so the page loses its top of hierarchy.
Flow: profile
Found on revision: f2181ae
Repro:

  1. set this state first: org; seed kiloclaw:fake-org-instance bf89f948-5a6d-4df0-a61e-e402494fcbe1 1df30c13-e3be-4035-9ddf-fec0bbc5fac4; uimode night; battery 20 discharging; relaunch; key back; the device in dark mode
  2. open the app on emulator-5602
  3. reach profile
  4. the capture shows the defect named below
    Observed: The screen title "Profile" is rendered in muted gray while the same title on profile-arabic.png and every other heading is white, so the page loses its top of hierarchy.
    Expected: the screen renders without this defect

[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.

[e8] ux-check: Rename session dialog, value unchanged, in dark mode — e8.png

[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.

[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 — e9-day.png

[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.

[e3] e3 finding 3: header right cluster inside the gutter, no Copy link control — e3.png

[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

[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 — e9-night.png

[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.

[e7] ux-check: header right cluster (PR badge + context pill) stays inside the gutter with a long total cost — e7-context-sheet.png

[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 landed SCENE e1 OK (e1-scene.log) and opened the dialog with the value unchanged (e1-dialog.xml text="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"…

[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… — e1-dialog.png

[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…

[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… — p4.png

[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] and android.widget.TextView $1,234,567.89 tappable [55,1086][1025,1142]; header geometry (e1-header.txt) shows nothing crossing the 1080px bezel, pill…

[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… — e1-header.png

Follow-ups (not changed here)

  • not proved live: e1 finding 1 (composer first line): open a session and type the six-line draft — the composer input's top edge shows whole glyphs, or a cleanly scrolled-out line, never a line cut in half; the still $OUT/e1.png is the visual proof. (no capture cited it)
  • not proved live: ux-check: In a shipped (non-DEV) build, Preferences shows no raw 'mobile-*' flag keys or 'default · not loaded' status; on a dev build the Feature flags block is present only as the documented dev-only debug surface. (no capture cited it)
  • not proved live: 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. (no capture cited it)

Owner manual verification

Owner verification is pending; these checks did not pass automatically.

  • no owner-manual scope.

Open findings (not fixed here)

  • not proved live: compose-filled.png is no longer on the host that took it, so no publish can carry it
  • not proved live: profile.png is no longer on the host that took it, so no publish can carry it
  • not proved live: script-probe.png is no longer on the host that took it, so no publish can carry it
  • not proved live: session-detail.png is no longer on the host that took it, so no publish can carry it
  • not proved live: session-permission.png is no longer on the host that took it, so no publish can carry it

e4

e1

Comment thread apps/mobile/src/components/agents/use-text-height.tsx Outdated
Comment thread apps/mobile/src/components/agents/chat-composer-input-height.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (19 files)
  • apps/mobile/src/components/agents/chat-composer-attachment-send.test.ts
  • apps/mobile/src/components/agents/chat-composer-input-height.test.ts
  • apps/mobile/src/components/agents/chat-composer-input-height.ts
  • apps/mobile/src/components/agents/chat-composer-input-row.mounted.test.tsx
  • apps/mobile/src/components/agents/chat-composer-input-row.tsx
  • apps/mobile/src/components/agents/chat-composer.test.ts
  • apps/mobile/src/components/agents/chat-composer.tsx
  • apps/mobile/src/components/agents/new-session-prompt-initial-prompt.test.ts
  • apps/mobile/src/components/agents/new-session-prompt.tsx
  • apps/mobile/src/components/agents/session-context-metrics.tsx
  • apps/mobile/src/components/agents/session-detail-content.test.ts
  • apps/mobile/src/components/agents/use-text-height.mounted.test.tsx
  • apps/mobile/src/components/agents/use-text-height.tsx
  • apps/mobile/src/components/kilo-chat/message-input-content.tsx
  • apps/mobile/src/components/kilo-chat/message-input-layout.ts
  • apps/mobile/src/components/screen-header.mounted.test.tsx
  • patches/@expo+router-server@57.0.9.patch
  • pnpm-lock.yaml
  • pnpm-workspace.yaml
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 Summary

The line-snap fix is sound for the common case, but the minHeight floor in the snap is derived from the requested line height rather than the measured native pitch, so a cap that floors at the minimum can still leave the first draft line clipped; no security, memory-leak, or header-overflow issues found.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/mobile/src/components/agents/use-text-height.tsx 99 minHeight floor is built from the requested line height, so a cap that floors at the minimum is not a whole number of measured native lines and can still clip the first visible line
apps/mobile/src/components/agents/chat-composer-input-height.ts 154 Doc comment says the caller passes the font-scaled line height; the caller passes the measured native pitch
Files Reviewed (21 files)
  • apps/mobile/plugins/branded-splash.test.ts - 0 issues
  • apps/mobile/src/components/agents/chat-composer-attachment-send.test.ts - 0 issues
  • apps/mobile/src/components/agents/chat-composer-input-height.test.ts - 0 issues
  • apps/mobile/src/components/agents/chat-composer-input-height.ts - 1 issue
  • apps/mobile/src/components/agents/chat-composer-input-row.mounted.test.tsx - 0 issues
  • apps/mobile/src/components/agents/chat-composer-input-row.tsx - 0 issues
  • apps/mobile/src/components/agents/chat-composer.test.ts - 0 issues
  • apps/mobile/src/components/agents/chat-composer.tsx - 0 issues
  • apps/mobile/src/components/agents/new-session-prompt-initial-prompt.test.ts - 0 issues
  • apps/mobile/src/components/agents/new-session-prompt.tsx - 0 issues
  • apps/mobile/src/components/agents/session-context-metrics.tsx - 0 issues
  • apps/mobile/src/components/agents/session-detail-content.test.ts - 0 issues
  • apps/mobile/src/components/agents/session-detail-content.tsx - 0 issues
  • apps/mobile/src/components/agents/use-text-height.mounted.test.tsx - 0 issues
  • apps/mobile/src/components/agents/use-text-height.tsx - 1 issue
  • apps/mobile/src/components/kilo-chat/message-input-content.tsx - 0 issues
  • apps/mobile/src/components/kilo-chat/message-input-layout.ts - 0 issues
  • apps/mobile/src/components/screen-header.mounted.test.tsx - 0 issues
  • patches/@expo+router-server@57.0.9.patch - 0 issues
  • pnpm-lock.yaml - 0 issues (generated)
  • pnpm-workspace.yaml - 0 issues
Notes
  • The content/SEO rules in the custom instructions target the kilocode-landing repo; this PR is the mobile app in Kilo-Org/cloud, so they do not apply. No .md files are changed, so no markdown image guidance is needed.
  • Memory-leak check: the new onContentSizeChange handler and useState are plain callbacks/state with no subscriptions or timers, and useTextHeight adds no listeners; no leak introduced.
  • The PR description says iOS is unchanged because it reports no native content height, but the agent composer passes nativeContentHeight on both platforms, so the iOS cap also snaps. The snap is line-aligned on iOS too, so this is a behavior change rather than a defect.

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic marked this pull request as draft September 22, 2026 07:22
@iscekic
iscekic force-pushed the kwf/explorer-5-visual-defects-on-android-in-the-message-input-0cd6e-41e5 branch from 3e1dee0 to 6ceeb85 Compare September 23, 2026 07:31
# 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.
main moved the PR badge out of the header's right slot onto the goal row
(#6562/#6614), so the cluster now holds only the context pill. Assert
that, and drop the import the assertion no longer needs.
@iscekic
iscekic marked this pull request as ready for review September 25, 2026 14:54
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 25, 2026
@iscekic
iscekic merged commit 600095b into main Sep 25, 2026
89 of 91 checks passed
@iscekic
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants