@easyops-cn/docusaurus-search-local 0.51.1 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/client/client/theme/SearchBar/SearchBar.jsx +11 -12
- package/dist/client/client/utils/keymap.js +75 -0
- package/dist/client/client/utils/platform.js +24 -0
- package/dist/server/server/utils/generate.js +2 -1
- package/dist/server/server/utils/validateOptions.js +1 -0
- package/dist/types/index.d.ts +11 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [0.52.0](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.51.1...v0.52.0) (2025-07-16)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* Add configurable keyboard shortcut for search bar focus ([92b32ee](https://github.com/easyops-cn/docusaurus-search-local/commit/92b32ee0ba0ee63664b3ae2cae9aa92b433025cf)), closes [#516](https://github.com/easyops-cn/docusaurus-search-local/issues/516)
|
|
11
|
+
|
|
5
12
|
## [0.51.1](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.51.0...v0.51.1) (2025-06-19)
|
|
6
13
|
|
|
7
14
|
|
|
@@ -9,10 +9,12 @@ import { useActivePlugin } from "@docusaurus/plugin-content-docs/client";
|
|
|
9
9
|
import { fetchIndexesByWorker, searchByWorker } from "../searchByWorker";
|
|
10
10
|
import { SuggestionTemplate } from "./SuggestionTemplate";
|
|
11
11
|
import { EmptyTemplate } from "./EmptyTemplate";
|
|
12
|
-
import { Mark, searchBarShortcut, searchBarShortcutHint, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, searchContextByPaths, hideSearchBarWithNoSearchContext, useAllContextsWithNoSearchContext, } from "../../utils/proxiedGenerated";
|
|
12
|
+
import { Mark, searchBarShortcut, searchBarShortcutHint, searchBarShortcutKeymap, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, searchContextByPaths, hideSearchBarWithNoSearchContext, useAllContextsWithNoSearchContext, } from "../../utils/proxiedGenerated";
|
|
13
13
|
import LoadingRing from "../LoadingRing/LoadingRing";
|
|
14
14
|
import { normalizeContextByPath } from "../../utils/normalizeContextByPath";
|
|
15
15
|
import { searchResultLimits } from "../../utils/proxiedGeneratedConstants";
|
|
16
|
+
import { parseKeymap, matchesKeymap, getKeymapHints } from "../../utils/keymap";
|
|
17
|
+
import { isMacPlatform } from "../../utils/platform";
|
|
16
18
|
import styles from "./SearchBar.module.css";
|
|
17
19
|
async function fetchAutoCompleteJS() {
|
|
18
20
|
const autoCompleteModule = await import("@easyops-cn/autocomplete.js");
|
|
@@ -291,9 +293,7 @@ export default function SearchBar({ handleSearchBarToggle, }) {
|
|
|
291
293
|
}
|
|
292
294
|
}, []);
|
|
293
295
|
// Implement hint icons for the search shortcuts on mac and the rest operating systems.
|
|
294
|
-
const isMac = isBrowser
|
|
295
|
-
? /mac/i.test(navigator.userAgentData?.platform ?? navigator.platform)
|
|
296
|
-
: false;
|
|
296
|
+
const isMac = isBrowser ? isMacPlatform() : false;
|
|
297
297
|
// Sync the input value and focus state for SSR
|
|
298
298
|
useEffect(() => {
|
|
299
299
|
const searchBar = searchBarRef.current;
|
|
@@ -312,13 +312,13 @@ export default function SearchBar({ handleSearchBarToggle, }) {
|
|
|
312
312
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
313
313
|
[]);
|
|
314
314
|
useEffect(() => {
|
|
315
|
-
if (!searchBarShortcut) {
|
|
315
|
+
if (!searchBarShortcut || !searchBarShortcutKeymap) {
|
|
316
316
|
return;
|
|
317
317
|
}
|
|
318
|
-
|
|
318
|
+
const parsedKeymap = parseKeymap(searchBarShortcutKeymap);
|
|
319
|
+
// Add shortcuts based on custom keymap
|
|
319
320
|
const handleShortcut = (event) => {
|
|
320
|
-
if ((
|
|
321
|
-
(event.key === "k" || event.key === "K")) {
|
|
321
|
+
if (matchesKeymap(event, parsedKeymap)) {
|
|
322
322
|
event.preventDefault();
|
|
323
323
|
searchBarRef.current?.focus();
|
|
324
324
|
onInputFocus();
|
|
@@ -328,7 +328,7 @@ export default function SearchBar({ handleSearchBarToggle, }) {
|
|
|
328
328
|
return () => {
|
|
329
329
|
document.removeEventListener("keydown", handleShortcut);
|
|
330
330
|
};
|
|
331
|
-
}, [
|
|
331
|
+
}, [onInputFocus, searchBarShortcutKeymap]);
|
|
332
332
|
const onClearSearch = useCallback(() => {
|
|
333
333
|
const params = new URLSearchParams(location.search);
|
|
334
334
|
params.delete(SEARCH_PARAM_HIGHLIGHT);
|
|
@@ -359,9 +359,8 @@ export default function SearchBar({ handleSearchBarToggle, }) {
|
|
|
359
359
|
searchBarShortcutHint &&
|
|
360
360
|
(inputValue !== "" ? (<button className={styles.searchClearButton} onClick={onClearSearch}>
|
|
361
361
|
✕
|
|
362
|
-
</button>) : (isBrowser && (<div className={styles.searchHintContainer}>
|
|
363
|
-
<kbd className={styles.searchHint}>{
|
|
364
|
-
<kbd className={styles.searchHint}>K</kbd>
|
|
362
|
+
</button>) : (isBrowser && searchBarShortcutKeymap && (<div className={styles.searchHintContainer}>
|
|
363
|
+
{getKeymapHints(searchBarShortcutKeymap, isMac).map((hint, index) => (<kbd key={index} className={styles.searchHint}>{hint}</kbd>))}
|
|
365
364
|
</div>)))}
|
|
366
365
|
</div>);
|
|
367
366
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { isMacPlatform } from './platform';
|
|
2
|
+
export function parseKeymap(keymap) {
|
|
3
|
+
const parts = keymap.toLowerCase().split('+');
|
|
4
|
+
const result = {
|
|
5
|
+
key: '',
|
|
6
|
+
ctrl: false,
|
|
7
|
+
alt: false,
|
|
8
|
+
shift: false,
|
|
9
|
+
meta: false,
|
|
10
|
+
};
|
|
11
|
+
// Detect if we're on Mac to handle 'mod' appropriately
|
|
12
|
+
const isMac = isMacPlatform();
|
|
13
|
+
for (const part of parts) {
|
|
14
|
+
const trimmed = part.trim();
|
|
15
|
+
switch (trimmed) {
|
|
16
|
+
case 'ctrl':
|
|
17
|
+
result.ctrl = true;
|
|
18
|
+
break;
|
|
19
|
+
case 'cmd':
|
|
20
|
+
result.meta = true;
|
|
21
|
+
break;
|
|
22
|
+
case 'mod':
|
|
23
|
+
if (isMac) {
|
|
24
|
+
result.meta = true;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
result.ctrl = true;
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
case 'alt':
|
|
31
|
+
result.alt = true;
|
|
32
|
+
break;
|
|
33
|
+
case 'shift':
|
|
34
|
+
result.shift = true;
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
result.key = trimmed;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
export function matchesKeymap(event, keymap) {
|
|
44
|
+
return (event.key.toLowerCase() === keymap.key &&
|
|
45
|
+
event.ctrlKey === keymap.ctrl &&
|
|
46
|
+
event.altKey === keymap.alt &&
|
|
47
|
+
event.shiftKey === keymap.shift &&
|
|
48
|
+
event.metaKey === keymap.meta);
|
|
49
|
+
}
|
|
50
|
+
export function getKeymapHints(keymap, isMac) {
|
|
51
|
+
const parsedKeymap = parseKeymap(keymap);
|
|
52
|
+
const hints = [];
|
|
53
|
+
// Handle original keymap string to detect 'mod' for proper hint display
|
|
54
|
+
const parts = keymap.toLowerCase().split('+').map(p => p.trim());
|
|
55
|
+
const hasMod = parts.includes('mod');
|
|
56
|
+
if (parsedKeymap.ctrl && !hasMod) {
|
|
57
|
+
hints.push('ctrl');
|
|
58
|
+
}
|
|
59
|
+
if (parsedKeymap.meta && !hasMod) {
|
|
60
|
+
hints.push(isMac ? '⌘' : 'cmd');
|
|
61
|
+
}
|
|
62
|
+
if (hasMod) {
|
|
63
|
+
hints.push(isMac ? '⌘' : 'ctrl');
|
|
64
|
+
}
|
|
65
|
+
if (parsedKeymap.alt) {
|
|
66
|
+
hints.push(isMac ? '⌥' : 'alt');
|
|
67
|
+
}
|
|
68
|
+
if (parsedKeymap.shift) {
|
|
69
|
+
hints.push(isMac ? '⇧' : 'shift');
|
|
70
|
+
}
|
|
71
|
+
if (parsedKeymap.key) {
|
|
72
|
+
hints.push(parsedKeymap.key.toUpperCase());
|
|
73
|
+
}
|
|
74
|
+
return hints;
|
|
75
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects if the current platform is macOS using modern, non-deprecated APIs.
|
|
3
|
+
* Falls back gracefully for older browsers or server-side rendering.
|
|
4
|
+
*/
|
|
5
|
+
export function isMacPlatform() {
|
|
6
|
+
// Handle server-side rendering or missing navigator
|
|
7
|
+
if (typeof navigator === 'undefined') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
// Try modern User-Agent Client Hints API first (if available)
|
|
11
|
+
if ('userAgentData' in navigator && navigator.userAgentData?.platform) {
|
|
12
|
+
const platform = navigator.userAgentData.platform.toLowerCase();
|
|
13
|
+
return platform.includes('mac');
|
|
14
|
+
}
|
|
15
|
+
// Fall back to user agent string parsing (more reliable than navigator.platform)
|
|
16
|
+
if (navigator.userAgent) {
|
|
17
|
+
return /mac/i.test(navigator.userAgent);
|
|
18
|
+
}
|
|
19
|
+
// Final fallback to deprecated navigator.platform for very old browsers
|
|
20
|
+
if (navigator.platform) {
|
|
21
|
+
return navigator.platform.toLowerCase().includes('mac');
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
@@ -6,7 +6,7 @@ const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
|
6
6
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
7
|
const getIndexHash_1 = require("./getIndexHash");
|
|
8
8
|
function generate(config, dir) {
|
|
9
|
-
const { language, removeDefaultStopWordFilter, removeDefaultStemmer, highlightSearchTermsOnTargetPage, searchResultLimits, searchResultContextMaxLength, explicitSearchResultPath, searchBarShortcut, searchBarShortcutHint, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, searchContextByPaths, hideSearchBarWithNoSearchContext, useAllContextsWithNoSearchContext, fuzzyMatchingDistance, } = config;
|
|
9
|
+
const { language, removeDefaultStopWordFilter, removeDefaultStemmer, highlightSearchTermsOnTargetPage, searchResultLimits, searchResultContextMaxLength, explicitSearchResultPath, searchBarShortcut, searchBarShortcutHint, searchBarShortcutKeymap, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, searchContextByPaths, hideSearchBarWithNoSearchContext, useAllContextsWithNoSearchContext, fuzzyMatchingDistance, } = config;
|
|
10
10
|
const indexHash = (0, getIndexHash_1.getIndexHash)(config);
|
|
11
11
|
const contents = [];
|
|
12
12
|
contents.push(`export const removeDefaultStemmer = ${JSON.stringify(removeDefaultStemmer)};`);
|
|
@@ -30,6 +30,7 @@ function generate(config, dir) {
|
|
|
30
30
|
contents.push(`export const explicitSearchResultPath = ${JSON.stringify(explicitSearchResultPath)};`);
|
|
31
31
|
contents.push(`export const searchBarShortcut = ${JSON.stringify(searchBarShortcut)};`);
|
|
32
32
|
contents.push(`export const searchBarShortcutHint = ${JSON.stringify(searchBarShortcutHint)};`);
|
|
33
|
+
contents.push(`export const searchBarShortcutKeymap = ${JSON.stringify(searchBarShortcutKeymap)};`);
|
|
33
34
|
contents.push(`export const searchBarPosition = ${JSON.stringify(searchBarPosition)};`);
|
|
34
35
|
contents.push(`export const docsPluginIdForPreferredVersion = ${docsPluginIdForPreferredVersion === undefined
|
|
35
36
|
? "undefined"
|
|
@@ -27,6 +27,7 @@ const schema = utils_validation_1.Joi.object({
|
|
|
27
27
|
ignoreCssSelectors: isStringOrArrayOfStrings.default([]),
|
|
28
28
|
searchBarShortcut: utils_validation_1.Joi.boolean().default(true),
|
|
29
29
|
searchBarShortcutHint: utils_validation_1.Joi.boolean().default(true),
|
|
30
|
+
searchBarShortcutKeymap: utils_validation_1.Joi.string().default("mod+k"),
|
|
30
31
|
searchBarPosition: utils_validation_1.Joi.string().default("auto"),
|
|
31
32
|
docsPluginIdForPreferredVersion: utils_validation_1.Joi.string(),
|
|
32
33
|
zhUserDict: utils_validation_1.Joi.string(),
|
package/dist/types/index.d.ts
CHANGED
|
@@ -124,6 +124,17 @@ export interface PluginOptions {
|
|
|
124
124
|
* @default true
|
|
125
125
|
*/
|
|
126
126
|
searchBarShortcutHint?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Custom keyboard shortcut to focus the search bar. Supports formats like:
|
|
129
|
+
* - "s" for single key
|
|
130
|
+
* - "ctrl+k" for key combinations
|
|
131
|
+
* - "cmd+k" for Command+K (Mac) / Ctrl+K (others)
|
|
132
|
+
* - "mod+k" for Command+K (Mac) / Ctrl+K (others) - recommended cross-platform option
|
|
133
|
+
* - "ctrl+shift+k" for multiple modifiers
|
|
134
|
+
*
|
|
135
|
+
* @default "mod+k"
|
|
136
|
+
*/
|
|
137
|
+
searchBarShortcutKeymap?: string;
|
|
127
138
|
/**
|
|
128
139
|
* The side of the navbar the search bar should appear on. By default,
|
|
129
140
|
* it will try to autodetect based on your docusaurus config according
|