Here’s a version that also hides the Obsidian app header when the softkeyboard is open to make it cover more of what Option to hide/show top bar(s) when note is scrolled & when keyboard appears/disappears - Feature requests - Obsidian Forum is asking for. Don’t forget the CSS in the first post of this thread!
exports.invoke = async (app) => {
const duration = 250;
document.body.style.setProperty('--headertransduration', `${duration}ms`);
const capacitorapp = window.Capacitor?.Plugins?.App;
let viewHeaderHeight = 0;
let keyboardopen = false;
let headerhidden = false;
let isAutoScrolling = false;
function adjustScroll(delta) {
const editor = app.workspace?.activeLeaf?.view?.editor;
if (!editor || app.workspace?.activeLeaf?.view?.file?.extension != 'md') return;
const scrollInfo = editor.getScrollInfo();
const start = scrollInfo.top;
const end = start + delta;
const startTime = performance.now();
function animate(time) {
isAutoScrolling = true;
const elapsed = time - startTime;
const progress = Math.min(elapsed / duration, 1);
const ease = progress < 0.5
? 2 * progress * progress
: -1 + (4 - 2 * progress) * progress;
editor.scrollTo(scrollInfo.left, start + (end - start) * ease);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
isAutoScrolling = false;
}
}
requestAnimationFrame(animate);
}
function hideHeader() {
if (headerhidden || !document.activeElement.classList.contains('cm-content') || document.activeElement.closest('.metadata-property-value') || document.activeElement.closest('.metadata-property-key')) return;
document.body.classList.add('hide-view-header');
adjustScroll(-viewHeaderHeight);
headerhidden = true;
}
function showHeader() {
if (!headerhidden) return;
document.body.classList.remove('hide-view-header');
adjustScroll(viewHeaderHeight);
headerhidden = false;
}
function elementDefocused() {
setTimeout(() => {
if(!document.activeElement.onblur) document.activeElement.onblur = elementDefocused;
if (keyboardopen) {
if (document.activeElement.classList.contains('cm-content')) {
hideHeader();
} else if (!document.activeElement.closest('.metadata-property-value') && !document.activeElement.closest('.metadata-property-key')) {
showHeader();
}
}
}, 100);
}
if (capacitorapp?.addListener) {
window.Capacitor?.Plugins?.Keyboard?.addListener('keyboardWillShow', () => {
keyboardopen = true;
hideHeader();
if(!document.activeElement.onblur) document.activeElement.onblur = elementDefocused;
});
window.Capacitor?.Plugins?.Keyboard?.addListener('keyboardWillHide', () => {
keyboardopen = false;
showHeader();
});
}
app.workspace.onLayoutReady(() => {
const leafContent = document.querySelector('.workspace-leaf-content:not([data-type="outgoing-link"]):not([data-type="backlink"]):not([data-type="undefined"])');
const viewHeader = leafContent?.querySelector('.view-header');
viewHeaderHeight = viewHeader ? viewHeader.getBoundingClientRect().height : 0;
});
if (window.__scroll_hook_installed || !app.isMobile) return;
let lastScrollPosition = 0;
let scrollFlexibility = 1;
function hookScroll(thing) {
if (!thing) return;
const proto = Object.getPrototypeOf(thing);
if (!proto || typeof proto.trigger !== "function" || proto.trigger.__scroll_hooked) return;
const orig = proto.trigger;
proto.trigger = function (eventName, data) {
try {
if (eventName === "markdown-scroll") {
if (isAutoScrolling) return;
if (data.scroll == 0 || data.scroll <= (lastScrollPosition - scrollFlexibility)) {
document.body.classList.remove('hide-view-header');
headerhidden = false;
lastScrollPosition = data.scroll;
} else if (data.scroll > (lastScrollPosition + scrollFlexibility)) {
document.body.classList.add('hide-view-header');
headerhidden = true;
lastScrollPosition = data.scroll;
}
}
} catch (err) {
console.error(err);
}
return orig.apply(this, arguments);
};
proto.trigger.__scroll_hooked = true;
}
const thingsToHookInto = [app?.workspace, app?.workspace?.rootSplit, app?.workspace?.activeLeaf];
for (const thing of thingsToHookInto) hookScroll(thing);
window.__scroll_hook_installed = true;
}