Merge pull request #2608 from szabgab/navigation-help

Add pop-up window showing the keyboard shortcuts
This commit is contained in:
Eric Huss 2025-05-15 01:24:55 +00:00 committed by GitHub
commit 481346812c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 129 additions and 20 deletions

View file

@ -656,3 +656,46 @@ html:not(.sidebar-resizing) .sidebar {
margin-inline-start: -14px;
width: 14px;
}
/* The container for the help popup that covers the whole window. */
#mdbook-help-container {
/* Position and size for the whole window. */
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* This uses flex layout (which is set in book.js), and centers the popup
in the window.*/
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
/* Dim out the book while the popup is visible. */
background: var(--overlay-bg);
}
/* The popup help box. */
#mdbook-help-popup {
box-shadow: 0 4px 24px rgba(0,0,0,0.15);
min-width: 300px;
max-width: 500px;
width: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--bg);
color: var(--fg);
border-width: 1px;
border-color: var(--theme-popup-border);
border-style: solid;
border-radius: 8px;
padding: 10px;
}
.mdbook-help-title {
text-align: center;
/* mdbook's margin for h2 is way too large. */
margin: 10px;
}

View file

@ -64,6 +64,8 @@
--copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%);
--footnote-highlight: #2668a6;
--overlay-bg: rgba(33, 40, 48, 0.4);
}
.coal {
@ -115,6 +117,8 @@
--copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);
--footnote-highlight: #4079ae;
--overlay-bg: rgba(33, 40, 48, 0.4);
}
.light, html:not(.js) {
@ -166,6 +170,8 @@
--copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%);
--footnote-highlight: #7e7eff;
--overlay-bg: rgba(200, 200, 205, 0.4);
}
.navy {
@ -217,6 +223,8 @@
--copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%);
--footnote-highlight: #4079ae;
--overlay-bg: rgba(33, 40, 48, 0.4);
}
.rust {
@ -266,6 +274,8 @@
--copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%);
--footnote-highlight: #d3a17a;
--overlay-bg: rgba(150, 150, 150, 0.25);
}
@media (prefers-color-scheme: dark) {

View file

@ -623,7 +623,7 @@ aria-label="Show hidden lines"></button>';
(function chapterNavigation() {
document.addEventListener('keydown', function(e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
if (e.altKey || e.ctrlKey || e.metaKey) {
return;
}
if (window.search && window.search.hasFocus()) {
@ -643,6 +643,55 @@ aria-label="Show hidden lines"></button>';
window.location.href = previousButton.href;
}
}
function showHelp() {
const container = document.getElementById('mdbook-help-container');
const overlay = document.getElementById('mdbook-help-popup');
container.style.display = 'flex';
// Clicking outside the popup will dismiss it.
const mouseHandler = event => {
if (overlay.contains(event.target)) {
return;
}
if (event.button !== 0) {
return;
}
event.preventDefault();
event.stopPropagation();
document.removeEventListener('mousedown', mouseHandler);
hideHelp();
};
// Pressing esc will dismiss the popup.
const escapeKeyHandler = event => {
if (event.key === 'Escape') {
event.preventDefault();
event.stopPropagation();
document.removeEventListener('keydown', escapeKeyHandler, true);
hideHelp();
}
};
document.addEventListener('keydown', escapeKeyHandler, true);
document.getElementById('mdbook-help-container')
.addEventListener('mousedown', mouseHandler);
}
function hideHelp() {
document.getElementById('mdbook-help-container').style.display = 'none';
}
// Usually needs the Shift key to be pressed
switch (e.key) {
case '?':
e.preventDefault();
showHelp();
break;
}
// Rest of the keys are only active when the Shift key is not pressed
if (e.shiftKey) {
return;
}
switch (e.key) {
case 'ArrowRight':
e.preventDefault();

View file

@ -33,13 +33,7 @@ window.search = window.search || {};
mark_exclude = ['text'],
marker = new Mark(content),
URL_SEARCH_PARAM = 'search',
URL_MARK_PARAM = 'highlight',
SEARCH_HOTKEY_KEYCODES = [83, 191], // `s` or `/`.
ESCAPE_KEYCODE = 27,
DOWN_KEYCODE = 40,
UP_KEYCODE = 38,
SELECT_KEYCODE = 13;
URL_MARK_PARAM = 'highlight';
let current_searchterm = '',
doc_urls = [],
@ -352,7 +346,7 @@ window.search = window.search || {};
return;
}
if (e.keyCode === ESCAPE_KEYCODE) {
if (e.key === 'Escape') {
e.preventDefault();
searchbar.classList.remove('active');
setSearchUrlParameters('',
@ -362,38 +356,38 @@ window.search = window.search || {};
}
showSearch(false);
marker.unmark();
} else if (!hasFocus() && SEARCH_HOTKEY_KEYCODES.includes(e.keyCode)) {
} else if (!hasFocus() && (e.key === 'S' || e.key === '/')) {
e.preventDefault();
showSearch(true);
window.scrollTo(0, 0);
searchbar.select();
} else if (hasFocus() && (e.keyCode === DOWN_KEYCODE
|| e.keyCode === SELECT_KEYCODE)) {
} else if (hasFocus() && (e.key === 'ArrowDown'
|| e.key === 'Enter')) {
e.preventDefault();
const first = searchresults.firstElementChild;
if (first !== null) {
unfocusSearchbar();
first.classList.add('focus');
if (e.keyCode === SELECT_KEYCODE) {
if (e.key === 'Enter') {
window.location.assign(first.querySelector('a'));
}
}
} else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE
|| e.keyCode === UP_KEYCODE
|| e.keyCode === SELECT_KEYCODE)) {
} else if (!hasFocus() && (e.key === 'ArrowDown'
|| e.key === 'ArrowUp'
|| e.key === 'Enter')) {
// not `:focus` because browser does annoying scrolling
const focused = searchresults.querySelector('li.focus');
if (!focused) {
return;
}
e.preventDefault();
if (e.keyCode === DOWN_KEYCODE) {
if (e.key === 'ArrowDown') {
const next = focused.nextElementSibling;
if (next) {
focused.classList.remove('focus');
next.classList.add('focus');
}
} else if (e.keyCode === UP_KEYCODE) {
} else if (e.key === 'ArrowUp') {
focused.classList.remove('focus');
const prev = focused.previousElementSibling;
if (prev) {
@ -401,7 +395,7 @@ window.search = window.search || {};
} else {
searchbar.select();
}
} else { // SELECT_KEYCODE
} else { // Enter
window.location.assign(focused.querySelector('a'));
}
}

View file

@ -63,6 +63,19 @@
<script src="{{ resource "toc.js" }}"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
{{#if search_enabled}}
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
{{/if}}
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>

View file

@ -667,7 +667,7 @@ fn build_header_links(html: &str) -> String {
static BUILD_HEADER_LINKS: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
});
static IGNORE_CLASS: &[&str] = &["menu-title"];
static IGNORE_CLASS: &[&str] = &["menu-title", "mdbook-help-title"];
let mut id_counter = HashMap::new();