Skip to content

fix(mobile): reveal new-session composer pills above the keyboard - #6471

Merged
iscekic merged 2 commits into
mainfrom
kwf/explorer-new-session-filled-kb-up-the-mode-and-model-pill-67091-b2c5
Sep 21, 2026
Merged

iscekic merged 2 commits into
mainfrom
kwf/explorer-new-session-filled-kb-up-the-mode-and-model-pill-67091-b2c5

Conversation

@iscekic

@iscekic iscekic commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

On the New session screen, with the keyboard up, the composer card's Mode and model pills sat under the keyboard because the card is the scroll body's first child and nothing scrolled it above the IME's clip line. The diff adds a reveal-scroll hook that parks the card's bottom edge at the lifted viewport's bottom, restores the prior offset when the keyboard hides, and defers to the user's own drags.

Changelog for users

  • On the New session screen, the Mode and model pills now sit fully above the keyboard instead of their lower half hiding under it.
  • With the keyboard up, tapping the Mode or the model pill still opens its picker, and the pills row stays visible.
  • When the keyboard closes, the form returns to the scroll position it had before, keeping the prompt's first line visible below the header.
  • Scrolling the form while the keyboard is up keeps your position; the automatic reveal yields to your drag.
  • If the composer shrinks while the keyboard is up, the form leaves the old reveal offset so the card's top stays visible.

Changelog for maintainers

  • Fixes explorer finding new-session-filled-kb-up: the composer card is the scroll body's first child, so the keyboard-lift viewport clipped its bottom row at content offset 0; automaticallyAdjustKeyboardInsets only scrolls the focused input and is inert on Android.
  • New useComposerRevealScroll hook scrolls the New session form to the minimum offset that puts the composer card's bottom edge at the lifted viewport's bottom; it changes only the content offset, never a size.
  • The reveal is driven by keyboardDidShow plus the ScrollView and composer-wrapper layouts, covering both platform commit orders (Android commits the lift after the show event, iOS before); the last commit wins and earlier calls are no-ops.
  • The drag flag is scoped to one keyboard session: a drag during the session overrules the reveal and the hide-restore, while a drag with the keyboard down cannot suppress the next session's reveal.
  • The first reveal captures the user's offset and keyboardDidHide restores it; without the restore the tall form stays parked and the card's top clips under the header.
  • When the card shrinks back to fit the lifted viewport, the stale reveal offset is undone immediately with the user's offset; uncommitted (zero) measurements never trigger a scroll.
  • NewSessionConfigureFormProps moved unchanged to a dedicated props file to keep the form component under the repo's max-lines cap.
  • Risk: the reveal assumes the composer card stays the ScrollView's first child; unit tests cover the offset math, both commit orders, drag scoping, and the restore paths.

E2E proof

Offset given back: after the IME is dismissed (Android back while it is up) the form returns to its own top — the composer card's top edge (rounded corner and the prompt's first line) is visible… — e4imeup.png

[e6] ux-check: On the repro state/device with the keyboard up on the New session screen, the mode and model pills row is fully visible above the keyboard (no lower half clipped). — Android emulator-5554: the keyboard-up dump e6-kb-system.txt shows the scroll viewport clipped at bounds="[0,213][1080,1517]" with Mode pill bounds="[68,642][301,715]" and model pill bounds="[319,642][623,715]", so both pills' bottom edge 715 is fully above the keyboard top 1517 (the same pills at the same y appear in e6-kb.txt, the app-window dump taken at the same moment as e6-kb.png); the keyboard-down dump e6-kb-down.txt shows the same screen's viewport at bounds="[0,213][1080,2337]", proving the 1517 frame is the keyboard lift and not the layout default; the composer card (bounds…

ux-check: On the repro state/device with the keyboard up on the New session screen, the mode and model pills row is fully visible above the keyboard (no lower half clipped). — e6-keyboard-up.png

[e10] ux-check: With the keyboard up, tapping the mode pill and the model pill each opens its picker and the pills row stays fully visible. — android emulator-5604; keyboard up proven by mInputShown=true (e10-kbup-ime.log) and IME inset top 1389 / touchable region [0,1517][1080,2400] (e10-kbup-window.log) while the pills read 'Mode: Code' [68,867][301,941] and 'Auto Efficient' [319,867][623,941] (e10-kbup.txt), bottom 941 above the IME top; the mode pill opened 'Select mode' with pills [68,896][301,970] (e10-mode-picker.txt) and the model pill opened 'Select model' with pills [319,896][623,970] (e10-model-picker.txt, e10c-scene.log); zero UX defects; the diff's reveal scroll is inert at this 1080x2400 geometry (card bottom already…

ux-check: With the keyboard up, tapping the mode pill and the model pill each opens its picker and the pills row stays fully visible. — e10c.png

ux-check: With the keyboard up, the composer card's top (rounded corner and the prompt's first line) remains visible below the header, not clipped. — e7.png

[e2] Stale-drag guard — android emulator-5554 — Android emulator-5554, KILO_E2E_IME=1: after the form was scrolled with the IME down and the prompt re-focused, e2-kbup.txt shows the composer's pill row content-desc="Mode: Code" ... bounds="[68,831][301,904]" fully above the IME top, which the keyboard-lift viewport reports as class="android.widget.ScrollView" ... bounds="[0,213][1080,1517]"; UX audit (functional) found no missing feedback, lost work or broken recovery on New session with the keyboard up/down, so no UX-DEFECT (captures e2-kb-up.png / e2-kb-down.png). Caveat: the composer card (~650 px) fitted the lifted viewport (1304…

[e2] Stale-drag guard — android emulator-5554 — e2e-mobile-app/e2-kb-up.png

[e2] Stale-drag guard — android emulator-5554

[e2] Stale-drag guard — android emulator-5554 — e2e-mobile-app/e2-kb-down.png

[e6] ux-check: On the repro state/device with the keyboard up on the New session screen, the mode and model pills row is fully visible above the keyboard (no lower half clipped).

[e6] ux-check: On the repro state/device with the keyboard up on the New session screen, the mode and model pills row is fully visible above the keyboard (no lower half clipped). — prior/e6-kb.png

[e4] Stale-drag guard: form scrolled with the keyboard hidden, prompt re-focused, mode/model pills above the keyboard — android emulator-5554, entry input KILO_E2E_IME=1 via device-state.sh (device-state.log: 'soft keyboard with a hardware keyboard: ... wanted 1'); parked replay files/replay-e4.json ran as one script call (e4-run.log: 'SCENE e4 OK'; first attempt MISSed only on a stale draft left by an earlier attempt, cleared by the signed-in-home restore); the keyboard was hidden for the scroll steps then up for the final tap (e4-logcat.log: 'onRequestHide at ORIGIN_IME reason HIDE_SOFT_INPUT_BY_BACK_KEY fromUser true' then 'onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser true'…

[e4] Stale-drag guard: form scrolled with the keyboard hidden, prompt re-focused, mode/model pills above the keyboard — scripted-shard2/e4.png

E2E proof — log excerpts

[e2] Stale-drag guard — android emulator-5554 -> pass :: Android emulator-5554, KILO_E2E_IME=1: after the form was scrolled with the IME down and the prompt re-focused, e2-kbup.txt shows the composer's pill row `content-desc="Mode: Code" ... bounds="[68,831][301,904]"` fully above the IME top, which the keyboard-lift viewport reports as `class="android.widget.ScrollView" ... bounds="[0,213][1080,1517]"`; UX audit (functional) found no missing feedback, lost work or broken recovery on New session with the keyboard up/down, so no UX-DEFECT (captures e2-kb-up.png / e2-kb-down.png). Caveat: the composer card (~650 px) fitted the lifted viewport (1304 px), so the reveal resolved to offset 0 at this device density and the pill row is above the IME with
[e1] Edge count: the same pill row stays above the soft keyboard while the promp -> pass :: jev read the digest: pass (confidence 1.00)
/home/igor_kilocode_ai/.local/share/kwf/sections/explorer-new-session-filled-kb-up-the-mode-and-model-pill-67091-b2c5/e2e-mobile-app/e2-kbup.txt
                                          </com.horcrux.svg.GroupView>
                                        </com.horcrux.svg.SvgView>
                                      </android.widget.Button>
<redacted>
                                    </android.view.ViewGroup>
                                  </android.widget.ScrollView>
                                </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-new-session-filled-kb-up-the-mode-and-model-pill-67091-b2c5/e2e-mobile-app/scripted-e1.log
android.widget.TextView Run on tappable [37,792][1045,838]
android.widget.Button Run on: Cloud Agent tappable [37,856][909,972]
android.widget.TextView Cloud Agent tappable [67,886][844,942]
android.widget.Button Refresh tappable [928,856][1043,972]
android.widget.TextView Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process. tappable [36,991][1044,1065]
android.widget.TextView Repository tappable [37,1111][1045,1157]
android.widget.Button Repository: Select repository tappable [37,1175][1043,1291]
android.widget.TextView Select repository tappable [67,1205][978,1261]
android.widget.Button Connect GitLab tappable [76,1358][1004,1404]
android.widget.TextView Connect GitLab tappable [76,1358][944,1404]
android.widget.TextView Connect GitLab in your browser, then return here to pick a repository. tappable [76,1432][1004,1524]
android.widget.Button Open GitLab tappable [76,1552][870,1667]
android.widget.TextView Open GitLab tappable [412,1586][594,1632]
android.widget.Button Refresh repositories tappable [888,1552][1004,1667]
android.widget.TextView Changes tappable [37,1753][1045,1799]
android.view.View Changes tappable [37,1817][1043,1936]
android.widget.RadioButton Leave changes tappable [46,1826][540,1927]
android.widget.TextView Leave changes tappable [182,1853][403,1899]
android.widget.RadioButton Commit and push tappable [540,1826][1034,1927]
android.widget.TextView Commit and push tappable [656,1853][917,1899]
android.widget.TextView Environment tappable [37,1982][1045,2028]
android.widget.TextView Default environment tappable [37,2046][1045,2092]
android.widget.Button Start session [37,2148][1043,2264]
android.widget.TextView Start session tappable [442,2182][638,2228]
Owner request

Explorer finding: new-session-filled-kb-up: The Mode and model pills are drawn under the keyboard, hiding their lower half, while the same row is fully visible with the keyboard down.

The user-agent explorer found this while using the app like a user.
One finding per item; the explorer never edits product code.

Flow: new-session-filled-kb-up
Found on revision: f2181ae

Repro:

  1. set this state first: credits 30; reviews 5; org; density 560; uimode day; battery 18 discharging; relaunch; the device in light mode
  2. open the app on emulator-5602
  3. reach new-session-filled-kb-up
  4. the capture shows the defect named below

Observed: The Mode and model pills are drawn under the keyboard, hiding their lower half, while the same row is fully visible with the keyboard down.
Expected: the screen renders without this defect

Evidence (from the device run):

[e9] ux-check: closing the keyboard returns the form to its keyboard-down scroll position with the pills row fully visible — android emulator-5554; one script call (e9-scene.json) tapped the composer then pressed back: e9-logcat.log shows 'InsetsController: show(ime(), fromIme=true)' (06:06:25.302) then 'InsetsController: hide(ime(), fromIme=true)' (06:06:26.034); the post-hide digest (e9-run.log: 'SCENE e9 OK', 'android.view.View New session tappable [111,102][1044,167]', 'android.widget.Button Mode: Code tappable [68,642][301,715]', 'android.widget.Button Start session tappable [37,2148][1043,2264]') is identical to the keyboard-up digest in e4-run.log, i.e. the same bounds before and after the hide, so the form…

[e9] ux-check: closing the keyboard returns the form to its keyboard-down scroll position with the pills row fully visible — e9.png

[e8] ux-check: with the keyboard up, grow the composer then delete back to the minimum — pills row visible, composer not parked/clipped — android emulator-5606: with the soft keyboard up (form-body ScrollView bounds="[0,278][1080,1517]") the grown prompt's mode/model pills stay inside the lifted viewport (text="Code" bounds="[146,880][223,926]", text="Auto Efficient" bounds="[347,881][546,927]"), and after clearing back to the minimum the composer sits at its natural offset (EditText bounds="[58,336][1024,567]", text="Code" bounds="[146,720][223,766]"), the same layout as keyboard-down (e8-kbd-down.xml), so it is not parked/clipped and no gap opens below it. Digests: e8a-scene.log/e8a.png (grown), e8c-scene.log/e8c.png…

[e8] ux-check: with the keyboard up, grow the composer then delete back to the minimum — pills row visible, composer not parked/clipped — e8a.png

[e6] ux-check: On the repro state/device with the keyboard up on the New session screen, the mode and model pills row is fully visible above the keyboard (no lower half clipped). — Android emulator-5554: the keyboard-up dump e6-kb-system.txt shows the scroll viewport clipped at bounds="[0,213][1080,1517]" with Mode pill bounds="[68,642][301,715]" and model pill bounds="[319,642][623,715]", so both pills' bottom edge 715 is fully above the keyboard top 1517 (the same pills at the same y appear in e6-kb.txt, the app-window dump taken at the same moment as e6-kb.png); the keyboard-down dump e6-kb-down.txt shows the same screen's viewport at bounds="[0,213][1080,2337]", proving the 1517 frame is the keyboard lift and not the layout default; the composer card (bounds…

[e6] ux-check: On the repro state/device with the keyboard up on the New session screen, the mode and model pills row is fully visible above the keyboard (no lower half clipped). — e6-keyboard-down.png

[e8] ux-check: with the keyboard up, grow the composer then delete back to the minimum — pills row visible, composer not parked/clipped

[e8] ux-check: with the keyboard up, grow the composer then delete back to the minimum — pills row visible, composer not parked/clipped — e8c.png

Follow-ups (not changed here)

  • not proved live: Baseline the reveal must not change: with the soft keyboard hidden (Android back while the IME is up) the same pill row is still fully visible and no reveal scroll has moved the form. Entry input: KILO_E2E_IME=1. (no capture cited it)
  • not proved live: Edge count: the same pill row stays above the soft keyboard while the prompt grows to the input's 160pt max height (the input scrolls internally). Entry input: KILO_E2E_IME=1. (no capture cited it)
  • not proved live: New session from the Agents tab: with the soft keyboard up the composer's mode/model pill row ('Mode: Code') sits fully above the keyboard, empty and then filled with the finding's own prompt (happy + empty in one flow). Entry input: KILO_E2E_IME=1 (device-state axis; the scene dump's keyboard_up must read 1). (no capture cited it)
  • not proved live: Stale-drag guard: on the New session screen the form is scrolled while the soft keyboard is hidden (the user reaches Start session), then the prompt is focused again — with the keyboard up the composer's mode/model pill row ('Mode: Code') sits fully above the keyboard. This is the flow the delivered hook left unrevealed (a drag before the keyboard session suppressed it). Entry input: KILO_E2E_IME=1. (no capture cited it)
  • not proved live: ux-check: Closing the keyboard returns the form to its keyboard-down scroll position with the pills row fully visible and no residual scroll or clipping. (no capture cited it)
  • not proved live: ux-check: With the keyboard up, type enough lines to grow the composer then delete them back to the minimum (and remove an attachment if present): the pills row stays fully visible and the composer is not left parked/clipped with a gap below it. (no capture cited it)

Open findings (not fixed here)

  • not proved live: new-session-filled-kb-up.png is no longer on the host that took it, so no publish can carry it

e4

Explorer finding: new-session-filled-kb-up: The Mode and model pills are drawn under the keyboard, hiding their lower half, while the same row is fully visible with the keyboard down.

The user-agent explorer found this while using the app like a user.
One finding per item; the explorer never edits product code.

Flow: new-session-filled-kb-up
Found on revision: f2181ae

Repro:
1. set this state first: credits 30; reviews 5; org; density 560; uimode day; battery 18 discharging; relaunch; the device in light mode
2. open the app on emulator-5602
3. reach new-session-filled-kb-up
4. the capture shows the defect named below

Observed: The Mode and model pills are drawn under the keyboard, hiding their lower half, while the same row is fully visible with the keyboard down.
Expected: the screen renders without this defect

Evidence (from the device run):
- ~/.local/share/kwf/findings/explorer-new-session-filled-kb-up-the-mode-and-model-pill-6709164d/new-session-filled-kb-up.png
@kilo-code-bot

kilo-code-bot Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (5 files)
  • apps/mobile/src/components/agents/new-session-configure-form.tsx
  • apps/mobile/src/components/agents/new-session-configure-form-props.ts
  • apps/mobile/src/components/agents/new-session-configure-form.test.ts
  • apps/mobile/src/components/agents/use-composer-reveal-scroll.ts
  • apps/mobile/src/components/agents/use-composer-reveal-scroll.test.ts

Verified: keyboard listeners are registered once and removed on unmount (no leak), the reveal is ref-driven offset-only with no render/scroll feedback loop, uncommitted/zero measurements are guarded before the shrink-restore branch, and the extracted props type matches the removed inline type with no other consumers. No issues found in the changed lines.


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

Review guidance: REVIEW.md from base branch main

@iscekic

iscekic commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

This description names a scenario the proof did not capture:

  • not proved live: Baseline the reveal must not change: with the soft keyboard hidden (Android back while the IME is up) the same pill row is still fully visible and no reveal scroll has moved the form. Entry input: KILO_E2E_IME=1. (no capture cited it)

A repeated proof run rebuilds the same evidence, so no proof run is dispatched for a named gap. Merging with this gap open is your decision.

@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 21, 2026
@iscekic iscekic self-assigned this Sep 21, 2026

@eshurakov eshurakov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after a shallow triage pass. No blocking findings.

@iscekic
iscekic merged commit 51db0aa into main Sep 21, 2026
28 checks passed
@iscekic
iscekic deleted the kwf/explorer-new-session-filled-kb-up-the-mode-and-model-pill-67091-b2c5 branch September 21, 2026 19:23
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