From 57628a2aa477e7b65b2b988fb766b8ed62786ccd Mon Sep 17 00:00:00 2001 From: archipelago Date: Tue, 4 Aug 2026 03:21:53 -0400 Subject: [PATCH] fix(aiui)+docs: keyboard-resize parity for standalone AIUI; companion handover note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operator-reported: keyboard opening in chat pads the tab bar and scrolls the page instead of scaling the chat window. Triage: neode-ui already ships interactive-widget=resizes-content + the --visual-viewport-height var, so mobile-web Chrome resizes correctly — but an Android WebView ignores that meta entirely, and the described pan-plus-padding is the adjustPan/edge-to-edge- without-IME-insets signature. Companion-side fix documented for handover in docs/companion-keyboard-viewport.md (manifest adjustResize, or IME insets when edge-to-edge, plus a chrome://inspect verification recipe). Web side gets the one real parity gap: AIUI's standalone index.html lacked the meta neode-ui has. Co-Authored-By: Claude Opus 5 (1M context) --- aiui/packages/app/index.html | 10 ++- docs/companion-keyboard-viewport.md | 99 +++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 docs/companion-keyboard-viewport.md diff --git a/aiui/packages/app/index.html b/aiui/packages/app/index.html index c852ae6d..b8f4ee77 100644 --- a/aiui/packages/app/index.html +++ b/aiui/packages/app/index.html @@ -2,7 +2,15 @@ - + + diff --git a/docs/companion-keyboard-viewport.md b/docs/companion-keyboard-viewport.md new file mode 100644 index 00000000..1163e076 --- /dev/null +++ b/docs/companion-keyboard-viewport.md @@ -0,0 +1,99 @@ +# Companion app — soft-keyboard viewport handover + +**Audience:** the companion (Android WebView wrapper) developer. +**Reported:** 2026-08-04 by the operator — in chat, when the soft keyboard opens, +padding is added to the bottom tab bar and the page scrolls; the chat window +should instead scale to the height that remains above the keyboard. + +## Why this is (most likely) companion-side + +The symptom described — content keeps its full height, the browser *pans/scrolls* +the focused input into view, and the fixed bottom bar picks up a visual gap — is +the classic Android `adjustPan` (or edge-to-edge-without-IME-insets) signature. + +The web side already implements the correct contract, verified in-repo: + +1. **`neode-ui/index.html`** carries + `interactive-widget=resizes-content` in its viewport meta. In Chrome 108+ + this makes the keyboard resize the **layout** viewport, so + `window.innerHeight` shrinks. +2. **`neode-ui/src/main.ts` → `syncViewportHeightVar()`** mirrors + `window.innerHeight` into the CSS var `--visual-viewport-height` on + `resize`, `orientationchange`, and `visualViewport.resize`. It deliberately + uses `innerHeight` (not `visualViewport.height`) so the value shares a + reference frame with `position: fixed` elements like the mobile tab bar. +3. **`neode-ui/src/style.css`** sizes the mobile layout (including the chat + iframe container) from + `var(--visual-viewport-height, 100dvh)` minus the tab-bar/safe-area vars. + +So on mobile web Chrome, the keyboard shrinks `innerHeight`, the var updates, +and the chat scales. **An Android WebView ignores the `interactive-widget` +meta entirely** — keyboard resize there is governed by the host app. If the +host pans instead of resizing, no amount of web CSS can fix it: the WebView's +`innerHeight` never changes and the system scrolls the page instead. + +## What the companion app should do + +Pick the branch that matches how the Activity is configured: + +### A. Not edge-to-edge (no `WindowCompat.setDecorFitsSystemWindows(window, false)`) + +Set the soft-input mode so the WebView is *resized*, not panned: + +```xml + + +``` + +`adjustPan` (and on some OEM builds the historical default `adjustUnspecified`) +produces exactly the reported behaviour. + +### B. Edge-to-edge (decorFitsSystemWindows = false) + +`adjustResize` alone stops working in edge-to-edge; you must consume the IME +inset yourself and resize the WebView: + +```kotlin +ViewCompat.setOnApplyWindowInsetsListener(webViewContainer) { view, insets -> + val ime = insets.getInsets(WindowInsetsCompat.Type.ime()) + val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + // Resize the container to end above the keyboard (or the nav bar when closed) + view.updatePadding(bottom = maxOf(ime.bottom, bars.bottom)) + WindowInsetsCompat.CONSUMED +} +``` + +(If targeting SDK 35+, edge-to-edge is enforced, so branch B is the one that +applies.) + +### Do NOT compensate on the web side + +Please don't inject extra bottom padding, margins, or scroll offsets into the +page from the wrapper — the web layout already subtracts the tab bar and safe +areas from `--visual-viewport-height`, and wrapper-side compensation double +counts (that is the "padding added to the tabs" half of the symptom). + +## How to verify the fix + +In the WebView's remote-debug console (`chrome://inspect`), focus the chat +input and check: + +- `window.innerHeight` **shrinks** by roughly the keyboard height → correct + (branch A/B working). The chat window will scale; no page scroll. +- `window.innerHeight` **unchanged** while `window.visualViewport.height` + shrinks → the WebView is still panning; the manifest/insets change hasn't + taken effect. + +## Web-side status (for completeness) + +- neode-ui (the page the companion actually loads): already correct, no change + needed. +- AIUI standalone (`aiui/packages/app/index.html`): was missing the + `interactive-widget` token; added 2026-08-04 for parity. Only affects AIUI + used outside a node, not the embedded/companion path. +- iOS Safari / iOS WKWebView: `interactive-widget` is not supported there. If + an iOS wrapper appears later, the equivalent is + `KeyboardLayoutGuide`/`keyboardWillChangeFrame` driving the WKWebView frame — + same principle: resize the web content, never pan it.