Skip to content

Instantly share code, notes, and snippets.

@Jehong-Ahn
Last active December 20, 2024 02:44
Show Gist options
  • Save Jehong-Ahn/2ffa68d580a0dac99e15dcda96885ad0 to your computer and use it in GitHub Desktop.
Save Jehong-Ahn/2ffa68d580a0dac99e15dcda96885ad0 to your computer and use it in GitHub Desktop.
Notion Heading Navigator
// ==UserScript==
// @name Notion Heading Navigator
// @namespace x.com/Jehong_Ahn
// @version 0.1
// @description Keyboard shortcuts for navigating between headings in Notion
// @author Jehong_Ahn
// @homepageURL https://x.com/Jehong_Ahn
// @updateURL https://gist.githubusercontent.com/Jehong-Ahn/2ffa68d580a0dac99e15dcda96885ad0/raw/notion-heading-navigator.js
// @downloadURL https://gist.githubusercontent.com/Jehong-Ahn/2ffa68d580a0dac99e15dcda96885ad0/raw/notion-heading-navigator.js
// @match https://www.notion.so/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Selector for Notion heading blocks
const HEADING_SELECTOR = '.notion-sub_header-block';
// Keyboard shortcut configuration
const KEY_CONFIG = {
next: {
key: ']',
alt: true
},
prev: {
key: '[',
alt: true
}
};
// Get all headings with their position information
function getVisibleHeadings() {
const headings = Array.from(document.querySelectorAll(HEADING_SELECTOR));
const viewportHeight = window.innerHeight;
return headings.map(heading => {
const rect = heading.getBoundingClientRect();
return {
element: heading,
top: rect.top,
// true if the heading is within the viewport
isVisible: rect.top >= 0 && rect.top <= viewportHeight
};
});
}
// Navigate to the next or previous heading
function moveToHeading(direction) {
const headings = getVisibleHeadings();
if (!headings.length) return;
// Find currently visible heading
const visibleHeading = headings.find(h => h.isVisible);
let targetHeading;
if (direction === 'next') {
// If there's a visible heading, find the next one
// If not, find the first heading below the current scroll position
targetHeading = visibleHeading
? headings.find(h => h.top > visibleHeading.top + 10)
: headings.find(h => h.top > 0);
} else {
// For previous direction, reverse the array and find the last heading above
const reversedHeadings = [...headings].reverse();
targetHeading = visibleHeading
? reversedHeadings.find(h => h.top < visibleHeading.top - 10)
: reversedHeadings.find(h => h.top < 0);
}
// Scroll to target heading if found
if (targetHeading) {
targetHeading.element.scrollIntoView({
behavior: 'smooth',
block: 'start' // Align heading to the top of the viewport
});
}
}
// Event listener for keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key === KEY_CONFIG.next.key && e.altKey === KEY_CONFIG.next.alt) {
e.preventDefault();
moveToHeading('next');
} else if (e.key === KEY_CONFIG.prev.key && e.altKey === KEY_CONFIG.prev.alt) {
e.preventDefault();
moveToHeading('prev');
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment