From 2ec3797ff3dda2871983e3b4c25c06cf1cf3e17b Mon Sep 17 00:00:00 2001 From: Saqeb Akhter Date: Sat, 31 Oct 2026 19:54:50 +0400 Subject: [PATCH] fix: focus mobile terminal on explicit session selection (#35) --- docs/reports/mobile-tab-keyboard/README.md | 22 +++ docs/reports/mobile-tab-keyboard/desktop.png | Bin 0 -> 48454 bytes docs/reports/mobile-tab-keyboard/mobile.png | Bin 0 -> 21385 bytes src/web/public/app.js | 34 ++--- src/web/public/mobile-tabs.js | 13 +- .../mobile-tab-keyboard-focus.browser.test.ts | 133 ++++++++++++++++++ test/mobile/tabs.test.ts | 4 +- 7 files changed, 185 insertions(+), 21 deletions(-) create mode 100644 docs/reports/mobile-tab-keyboard/README.md create mode 100644 docs/reports/mobile-tab-keyboard/desktop.png create mode 100644 docs/reports/mobile-tab-keyboard/mobile.png create mode 100644 test/mobile-tab-keyboard-focus.browser.test.ts diff --git a/docs/reports/mobile-tab-keyboard/README.md b/docs/reports/mobile-tab-keyboard/README.md new file mode 100644 index 000000000..bbc433ebb --- /dev/null +++ b/docs/reports/mobile-tab-keyboard/README.md @@ -0,0 +1,22 @@ +# Mobile tab keyboard focus (#35) + +Root cause: explicit touch selection shared a policy that only focused the terminal +when KeyboardHandler already considered the keyboard visible; reselecting the active +tab returned before focusing. The fork's tab sheet also scheduled search focus 180ms +after opening, without cancelling it when a session was selected. A quick selection +therefore focused the terminal and then the closed sheet's offscreen search input. + +The fix focuses terminal input synchronously for explicit selections, including the +active tab, preserves passive app-driven restore when the keyboard is hidden, and +cancels/guards delayed search focus before closing the sheet and selecting a session. + +Chromium phone-emulation checks used two isolated disposable shell sessions on a +unique data directory and tmux socket. Switching tabs and quick active-tab reselection +kept focus on xterm's textarea after the delay; the sheet closed and no page errors +occurred. Screenshots show that isolated UI. No production session input was used. +Headless Chromium validates DOM focus, not native iOS keyboard presentation. WebKit +was unavailable on this machine; real-device iOS verification remains necessary. + +Six focused browser checks and 172 related tests passed. The broader smoke retained +the two previously recorded long-scrollback wheel failures; no new focus/browser +errors appeared. See the GitHub issue for build, deployment and CI gates. diff --git a/docs/reports/mobile-tab-keyboard/desktop.png b/docs/reports/mobile-tab-keyboard/desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..3ff7f0b364f023b37b37ebba08f03729dfd34459 GIT binary patch literal 48454 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 Mtw>VTy;}zBgjz)PbKakJxi1^o2i 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 literal 0 HcmV?d00001 diff --git a/src/web/public/app.js b/src/web/public/app.js index 89a126e94..0458d90a6 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -10275,9 +10275,9 @@ class CodemanApp { // instead of switching to it; on a window too narrow for the grid it is an // ordinary click. if ((event?.ctrlKey || event?.metaKey) && this.addSessionToTiles?.(sessionId)) return; - // On touch with the keyboard hidden, blur the tapped tab so switching - // sessions doesn't pop the on-screen keyboard. Focus policy itself lives - // in selectSession via _shouldFocusTerminalForTabSwitch(). + // A hidden mobile keyboard can leave the terminal textarea focused. Blur + // first so this explicit tap can reopen it with a fresh synchronous focus. + // Automatic selections keep their passive focus policy in selectSession(). const keyboardOpen = typeof KeyboardHandler !== 'undefined' && KeyboardHandler.keyboardVisible === true; if (!keyboardOpen && MobileDetection.isTouchDevice()) { document.activeElement?.blur?.(); @@ -11590,11 +11590,12 @@ class CodemanApp { bar.hidden = false; } - _shouldFocusTerminalForTabSwitch() { + _shouldFocusTerminalForTabSwitch(options = {}) { if (typeof MobileDetection === 'undefined' || !MobileDetection.isTouchDevice()) { return true; } - return typeof KeyboardHandler !== 'undefined' && KeyboardHandler.keyboardVisible; + return options.auto !== true || + (typeof KeyboardHandler !== 'undefined' && KeyboardHandler.keyboardVisible); } async selectSession(sessionId, options = {}) { @@ -11626,6 +11627,14 @@ class CodemanApp { // fails toward acknowledging rather than toward an alert that can never be // cleared. const userInitiated = options?.auto !== true; + const focusTerminalWithoutActivation = () => { + const focusTerminal = () => this.terminal.focus(); + if (this._terminalViewerActivity?.runWithoutActivation) { + this._terminalViewerActivity.runWithoutActivation(focusTerminal); + } else { + focusTerminal(); + } + }; // Terminal sizing activity currently shares this audited `auto: true` // intent contract: app-restored selections stay passive, human choices own. // `!activeWebviewId`: with a dashboard foregrounded, re-selecting the @@ -11641,6 +11650,7 @@ class CodemanApp { this.applyTerminalFontPolicy?.(this.sessions.get(sessionId)?.mode); try { this.syncTerminalGeometry?.(); } catch {} this._terminalViewerActivity?.activate('session-select'); + if (this.terminal && this._shouldFocusTerminalForTabSwitch(options)) focusTerminalWithoutActivation(); } return; } @@ -11685,17 +11695,9 @@ class CodemanApp { // programmatic focus() within the user-gesture call stack (e.g. tab click). // After the first await the gesture context is lost and focus() is silently // ignored, leaving the keyboard unable to send input to the terminal. - // Desktop always focuses; touch focuses only while the on-screen keyboard - // is already open (so a tab switch doesn't pop the keyboard). - const shouldFocusTerminal = this._shouldFocusTerminalForTabSwitch(); - const focusTerminalWithoutActivation = () => { - const focusTerminal = () => this.terminal.focus(); - if (this._terminalViewerActivity?.runWithoutActivation) { - this._terminalViewerActivity.runWithoutActivation(focusTerminal); - } else { - focusTerminal(); - } - }; + // Explicit mobile selection opens the keyboard. App-driven selection only + // preserves an already-visible keyboard; boot/replay must not pop it open. + const shouldFocusTerminal = this._shouldFocusTerminalForTabSwitch(options); if (shouldFocusTerminal && this.terminal) { focusTerminalWithoutActivation(); } diff --git a/src/web/public/mobile-tabs.js b/src/web/public/mobile-tabs.js index b0040016a..414b28dfa 100644 --- a/src/web/public/mobile-tabs.js +++ b/src/web/public/mobile-tabs.js @@ -431,13 +431,18 @@ const MobileTabSheet = { const scrim = document.getElementById('mobileTabScrim'); const pill = document.getElementById('mobileTabPill'); if (!sheet || !scrim) return; + clearTimeout(this._searchFocusTimer); + this._searchFocusTimer = null; const willOpen = typeof open === 'boolean' ? open : !sheet.classList.contains('open'); sheet.classList.toggle('open', willOpen); scrim.classList.toggle('open', willOpen); if (pill) pill.setAttribute('aria-expanded', willOpen ? 'true' : 'false'); if (willOpen) { this.render(); - setTimeout(() => document.getElementById('mobileTabSearch')?.focus(), 180); + this._searchFocusTimer = setTimeout(() => { + this._searchFocusTimer = null; + if (this.isOpen()) document.getElementById('mobileTabSearch')?.focus(); + }, 180); document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; @@ -547,10 +552,12 @@ const MobileTabSheet = { listEl.innerHTML = html.join(''); }, activate(id) { + // Retire delayed search focus before handing the gesture to the terminal. + this.toggle(false); try { - window.app.selectSession(id); + if (window.app.handleSessionTabClick) window.app.handleSessionTabClick(null, id); + else window.app.selectSession(id); } catch {} - this.toggle(false); }, closeTab(id) { try { diff --git a/test/mobile-tab-keyboard-focus.browser.test.ts b/test/mobile-tab-keyboard-focus.browser.test.ts new file mode 100644 index 000000000..8150865e0 --- /dev/null +++ b/test/mobile-tab-keyboard-focus.browser.test.ts @@ -0,0 +1,133 @@ +/** Real browser DOM/focus regression; uses exact methods, no live sessions or native keyboard claims. */ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import ts from 'typescript'; +import { chromium, type Browser, type Page } from 'playwright'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +const publicDir = resolve(import.meta.dirname, '../src/web/public'); +function method(file: string, name: string): string { + const source = readFileSync(resolve(publicDir, file), 'utf8'); + const tree = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS); + let result = ''; + const visit = (node: ts.Node) => { + if (ts.isMethodDeclaration(node) && node.name.getText(tree) === name) result = node.getText(tree); + ts.forEachChild(node, visit); + }; + visit(tree); + if (!result) throw new Error(`Missing ${name}`); + return result; +} +const methods = ['handleSessionTabClick', '_shouldFocusTerminalForTabSwitch', 'selectSession'] + .map((name) => method('app.js', name)) + .join(','); +async function harness(browser: Browser, touch = true): Promise { + const page = await browser.newPage({ + viewport: { width: touch ? 393 : 1280, height: 852 }, + hasTouch: touch, + isMobile: touch, + }); + await page.setContent( + '' + ); + await page.addScriptTag({ + content: ` + window.KeyboardHandler={keyboardVisible:false};window.MobileDetection={isTouchDevice:()=>${touch}}; + window._crashDiag={log(){}};window.focusCalls=0; + window.app={${methods},activeSessionId:'a',isSoloWindow:true,detachedSessions:new Set(), + sessions:new Map([['b',{name:'b'}]]),_selectGeneration:0, + terminal:{focus(){focusCalls++;document.getElementById('terminal').focus()}}, + _clearTimer(){},_flushLocalEchoTo(){},_setTerminalLoadState(){},_cleanupPreviousSession(){throw Error('STOP_BEFORE_SESSION_IO')},markIdleAlertSeen(){}}; + const select=app.selectSession;app.selectSession=(...args)=>select.apply(app,args).catch(e=>{ + if(e.message!=='STOP_BEFORE_SESSION_IO')throw e; + }); + document.getElementById('same').onclick=e=>app.handleSessionTabClick(e,'a'); + document.getElementById('other').onclick=e=>app.handleSessionTabClick(e,'b'); + `, + }); + return page; +} +describe('explicit mobile tab keyboard focus', () => { + let browser: Browser; + beforeAll(async () => { + browser = await chromium.launch({ headless: true }); + }); + afterAll(async () => { + await browser.close(); + }); + for (const target of ['same', 'other']) { + it(`focuses the terminal synchronously for a closed-keyboard ${target} tab tap`, async () => { + const page = await harness(browser); + try { + await page.locator('#terminal').focus(); // stale textarea focus with keyboard gone + await page.locator(`#${target}`).tap(); + expect(await page.evaluate(() => document.activeElement?.id)).toBe('terminal'); + expect(await page.evaluate('focusCalls')).toBe(1); + } finally { + await page.close(); + } + }); + } + it('direct same-session selection also restores terminal focus', async () => { + const page = await harness(browser); + try { + await page.evaluate('app.selectSession("a")'); + expect(await page.evaluate(() => document.activeElement?.id)).toBe('terminal'); + } finally { + await page.close(); + } + }); + it('keeps keyboard-hidden automatic selection passive, and preserves an existing keyboard', async () => { + const page = await harness(browser); + try { + await page.evaluate('app.selectSession("b",{auto:true})'); + expect(await page.evaluate('focusCalls')).toBe(0); + await page.evaluate('KeyboardHandler.keyboardVisible=true;app.selectSession("b",{auto:true})'); + expect(await page.evaluate('focusCalls')).toBe(1); + } finally { + await page.close(); + } + }); + it('preserves desktop selection focus', async () => { + const page = await harness(browser, false); + try { + await page.locator('#other').click(); + expect(await page.evaluate(() => document.activeElement?.id)).toBe('terminal'); + } finally { + await page.close(); + } + }); +}); + +describe('mobile sheet delayed search focus', () => { + let browser: Browser; + beforeAll(async () => { + browser = await chromium.launch({ headless: true }); + }); + afterAll(async () => { + await browser.close(); + }); + it('cancels pending search focus on a quick selection and leaves input on the terminal', async () => { + const page = await harness(browser); + try { + await page.addStyleTag({ content: readFileSync(resolve(publicDir, 'mobile.css'), 'utf8') }); + await page.evaluate(() => { + document.body.insertAdjacentHTML( + 'beforeend', + '
' + ); + }); + await page.addScriptTag({ + content: `window.MobileTabSheet={${['toggle', 'activate', 'isOpen'].map((n) => method('mobile-tabs.js', n)).join(',')},render(){}};`, + }); + await page.evaluate('MobileTabSheet.toggle(true);MobileTabSheet.activate("b")'); + expect(await page.evaluate(() => document.activeElement?.id)).toBe('terminal'); + await page.waitForTimeout(320); + expect(await page.evaluate(() => document.activeElement?.id)).toBe('terminal'); + expect(await page.evaluate('MobileTabSheet.isOpen()')).toBe(false); + await page.screenshot({ path: '/tmp/mobile-tab-keyboard-focus.png' }); + } finally { + await page.close(); + } + }); +}); diff --git a/test/mobile/tabs.test.ts b/test/mobile/tabs.test.ts index fad84a4e6..e6340303b 100644 --- a/test/mobile/tabs.test.ts +++ b/test/mobile/tabs.test.ts @@ -800,7 +800,7 @@ describe('Tab Navigation', () => { }); describe('Tab Touch Focus', () => { - it('switching tabs with the keyboard closed does not leave the terminal textarea focused', async () => { + it('a closed-keyboard tab click retires stale textarea focus before explicit selection', async () => { const { context, page } = await createDevicePage(standardPhone, baseUrl, 'chromium'); try { await page.waitForTimeout(WAIT.PAGE_SETTLE); @@ -836,7 +836,7 @@ describe('Tab Navigation', () => { expect(result.hasHandler).toBe(true); expect(result.selected).toBe('mock-session-2'); - expect(result.preserveKeyboard).toBe(false); + expect(result.preserveKeyboard).toBeUndefined(); expect(result.activeIsTextarea).toBe(false); } finally { await context.close();