diff --git a/server/routes/widgets.js b/server/routes/widgets.js index 0e09157..4489377 100644 --- a/server/routes/widgets.js +++ b/server/routes/widgets.js @@ -1206,6 +1206,14 @@ function renderDirectorySearch(c) { // ----- on-screen keyboard (drives the same filter path as typing) ----- if (cfg.show_onscreen_keyboard) { + /* Tell the platform not to raise ITS keyboard for this field. We autofocus a real + , which on Android is the signal to throw the system IME over the bottom of + the screen - directly on top of the keyboard we draw below, so a directory panel + showed Google's keyboard (mic, GIF and emoji keys included) and never showed its + own. The buttons write input.value directly, so suppressing the platform keyboard + costs nothing here. Ignored by browsers that don't know inputmode, which is the + right fallback: a desktop preview keeps behaving exactly as before. */ + input.setAttribute('inputmode', 'none'); var kb = document.getElementById('keyboard'); function press(ch) { input.value += ch; try { input.focus(); } catch(e){} onInput(); } ['1234567890','qwertyuiop','asdfghjkl','zxcvbnm'].forEach(function(r){ diff --git a/server/test/directory-search.test.js b/server/test/directory-search.test.js index d8bf97c..fbe2a13 100644 --- a/server/test/directory-search.test.js +++ b/server/test/directory-search.test.js @@ -59,6 +59,24 @@ test('show_onscreen_keyboard flag is carried into the page config', async () => assert.ok(html.includes('"show_onscreen_keyboard":false'), 'keyboard flag inlined (page hides the keyboard when false)'); }); +test('the built-in keyboard suppresses the platform one', async () => { + // The page autofocuses a real , which on Android raises the system IME over the + // bottom of the screen - covering the keyboard this widget draws itself. A panel showed + // Gboard, complete with a mic key, and never showed its own keyboard. + seed('search_kb_on', 'directory-search', { source_widget_id: 'board1', show_onscreen_keyboard: true }); + const { html } = await fetchRender('search_kb_on'); + assert.ok(/setAttribute\(\s*'inputmode'\s*,\s*'none'\s*\)/.test(html), + 'page tells the platform not to raise its own keyboard'); + + // Only when we are drawing one. With the built-in keyboard off there is nothing to cover, + // and the platform keyboard is the only way left to type. + seed('search_kb_off2', 'directory-search', { source_widget_id: 'board1', show_onscreen_keyboard: false }); + const off = await fetchRender('search_kb_off2'); + assert.ok(off.html.includes('"show_onscreen_keyboard":false'), 'flag inlined as false'); + assert.ok(!/inputmode="none"/.test(off.html), + 'the input is not statically marked inputmode=none - suppression is gated on the flag at runtime'); +}); + test('missing source -> friendly fallback page, not a 500', async () => { seed('search_missing', 'directory-search', { source_widget_id: 'does-not-exist' }); const { status, html } = await fetchRender('search_missing');