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');