Let’s face it: SilverBullet on mobile is a nightmare for reading. You try to scroll, and the keyboard jumps out like a jack-in-the-box. It’s annoying, it’s clunky, and I’m done with it.
I’ve built a script (together with Gemini) that forces the editor to stay in “ReadOnly” mode (not the real SilverBullet-ReadOnly, but a virtualized one) until you actually intend to write.
What it does:
- Blocks Single Taps: Poking the text does nothing. No cursor, no keyboard, no jumping screen.
- Scroll Freely: You can swipe and navigate without the UI losing its mind.
- The Unlock: Double-tap or Long-press the editor to summon the keyboard. Once you click away, it locks itself again.
Why it works: We went nuclear. Instead of asking the editor to behave, we used a Capture Phase interceptor on #sb-editor. It catches tap signals at the window level and deletes them before the browser can trigger the “helpful” keyboard pop-up.
It turns your notes back into a document, not a minefield.
I added a command to trigger it: Mobile: Toggle Accidental Tap Shield"
and also included in the commets a disabled event listened if you want to enable it permanently or PageLoaded.
-- The Streamlined Handshake (v7) with Toggle Command
-- Supports both Double-Tap OR Long-Press to unlock.
function setupStreamlinedShield()
-- If the shield is already globally marked as Active, don't inject again
if js.window.sbStreamlinedActive then return end
local scriptContent = [[
(function() {
let lastTap = 0;
let holdTimer = null;
const DOUBLE_TAP_THRESHOLD = 300;
const LONG_PRESS_THRESHOLD = 500;
let isUnlocked = false;
const unlock = () => {
isUnlocked = true;
console.log("Jarvis: Perimeter disarmed. Editing enabled.");
};
const lock = () => {
isUnlocked = false;
};
const handleCapture = (e) => {
// Critical: If the user toggled the shield OFF, stop intercepting immediately
if (window.sbShieldDisabledManually) return;
const isEditor = e.target.closest('#sb-editor');
const isUI = e.target.closest('.sb-actions, .sb-top-bar, .sb-sidebar, button, .menu-item');
if (!isEditor || isUI) return;
if (isUnlocked && document.activeElement.closest('#sb-editor')) return;
const now = Date.now();
if (e.type === 'touchstart') {
if (now - lastTap < DOUBLE_TAP_THRESHOLD) {
unlock();
}
lastTap = now;
clearTimeout(holdTimer);
holdTimer = setTimeout(() => {
unlock();
const el = document.querySelector('#sb-main .cm-content');
if (el) {
el.setAttribute('inputmode', 'text');
el.focus();
}
}, LONG_PRESS_THRESHOLD);
}
if (e.type === 'touchend' || e.type === 'touchmove') {
clearTimeout(holdTimer);
}
if (!isUnlocked) {
const killZone = ['mousedown', 'mouseup', 'click', 'touchend'];
if (killZone.includes(e.type)) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
}
};
document.addEventListener('focusout', (e) => {
if (e.target.closest('#sb-editor')) lock();
}, true);
['touchstart', 'touchend', 'touchmove', 'mousedown', 'mouseup', 'click'].forEach(type => {
window.addEventListener(type, handleCapture, { capture: true, passive: false });
});
window.sbStreamlinedActive = true;
console.log("Jarvis: v7 Streamlined Shield is operational.");
})();
]]
local scriptEl = js.window.document.createElement("script")
scriptEl.innerHTML = scriptContent
js.window.document.body.appendChild(scriptEl)
end
command.define {
name = "Mobile: Toggle Accidental Tap Shield",
run = function()
if not js.window.sbShieldDisabledManually then
-- Turn Shield OFF
js.window.sbShieldDisabledManually = true
editor.flashNotification("Shield DISARMED. Normal tapping restored.", "info")
else
-- Turn Shield ON
js.window.sbShieldDisabledManually = false
setupStreamlinedShield()
editor.flashNotification("Shield ENGAGED. Double-tap/Hold to edit.", "info")
end
end
}
-- Auto-init on load uncomment following line to enable it on pageLoaded event.
-- event.listen { name = "editor:pageLoaded", run = function() setupStreamlinedShield() end }
