🎉 Feat: add support for [Fusejs search](https://fusejs.io/) (#203)

This commit is contained in:
Cell
2022-12-03 18:43:22 +08:00
parent 5467baf56b
commit e3e152b2d0
13 changed files with 129 additions and 15 deletions
+1
View File
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
> Snapshot: <>
- :tada: Feat: add support for [Fusejs search](https://fusejs.io/) ([#203](https://github.com/hugo-fixit/FixIt/issues/203))
- :sparkles: Feat: add feature post reward support ([#216](https://github.com/hugo-fixit/FixIt/issues/216), @Lruihao[#234](https://github.com/hugo-fixit/FixIt/pull/234))
- :sparkles: Feat: add reward shortcode support ([#216](https://github.com/hugo-fixit/FixIt/issues/216))
- :sparkles: Feat: add TagCloud config support for tags page, see parameter `params.tagcloud` ([#235](https://github.com/hugo-fixit/FixIt/issues/235))
+3 -1
View File
@@ -136,7 +136,7 @@ You could add your FixIt site to the list in **nickname** lexicographical order
* **Sub Menu** supported
* **Content Encryption** supported (Pages, Partial)
* **Friends** page embedded template
* **Search** supported by [Lunr.js](https://lunrjs.com/) or [algolia](https://www.algolia.com/)
* **Search** supported by [Lunr.js](https://lunrjs.com/) or [algolia](https://www.algolia.com/) or [Fuse.js](https://fusejs.io/)
* **Twemoji** supported
* Automatically **highlighting** code
* **Copy code** to clipboard with one click
@@ -210,6 +210,7 @@ Make sure that you follow [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) while contrib
* [autocomplete-js](https://github.com/algolia/autocomplete)
* [Lunr.js](https://lunrjs.com/)
* [algoliasearch](https://github.com/algolia/algoliasearch-client-javascript)
* [Fuse.js](https://fusejs.io/)
* [lazysizes](https://github.com/aFarkas/lazysizes)
* [object-fit-images](https://github.com/fregante/object-fit-images)
* [Twemoji](https://github.com/twitter/twemoji)
@@ -244,6 +245,7 @@ Make sure that you follow [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) while contrib
<summary>The FixIt also draws on some features of the following projects, and thanks to their authors as well:</summary>
* [DoIt](https://github.com/HEIGE-PCloud/DoIt)
* [NexT](https://github.com/next-theme/hexo-theme-next)
</details>
+3 -1
View File
@@ -138,7 +138,7 @@ FixIt 主题继承了 LoveIt 等主题的优秀功能,并在它们的基础上
* 支持**二级菜单**
* 支持**内容加密**(页面、局部)
* 支持**友情链接**的页面模板
* 支持基于 [Lunr.js](https://lunrjs.com/) 或 [algolia](https://www.algolia.com/) 的**搜索**
* 支持基于 [Lunr.js](https://lunrjs.com/) 或 [algolia](https://www.algolia.com/) 或 [Fuse.js](https://fusejs.io/) 的**搜索**
* 支持 **Twemoji**
* 支持**代码高亮**
* 一键**复制代码**到剪贴板
@@ -212,6 +212,7 @@ FixIt 支持下列语言:
* [autocomplete-js](https://github.com/algolia/autocomplete)
* [Lunr.js](https://lunrjs.com/)
* [algoliasearch](https://github.com/algolia/algoliasearch-client-javascript)
* [Fuse.js](https://fusejs.io/)
* [lazysizes](https://github.com/aFarkas/lazysizes)
* [object-fit-images](https://github.com/fregante/object-fit-images)
* [Twemoji](https://github.com/twitter/twemoji)
@@ -246,6 +247,7 @@ FixIt 支持下列语言:
<summary>FixIt 主题还借鉴了以下项目的部分功能,同样感谢它们的作者:</summary>
* [DoIt](https://github.com/HEIGE-PCloud/DoIt)
* [NexT](https://github.com/next-theme/hexo-theme-next)
</details>
+2
View File
@@ -35,6 +35,8 @@ libFiles:
erudaJS: eruda@2.5.0/eruda.js
# fontawesome-free@6.2.0 https://github.com/FortAwesome/Font-Awesome
fontawesomeFreeCSS: '@fortawesome/fontawesome-free@6.2.0/css/all.min.css'
# fusejs@6.6.2 https://github.com/krisk/fuse
fuseJS: fuse.js@6.6.2/dist/fuse.min.js
# gitalk@1.7.2 https://github.com/gitalk/gitalk
gitalkCSS: gitalk@1.7.2/dist/gitalk.min.css
gitalkJS: gitalk@1.7.2/dist/gitalk.min.js
+2
View File
@@ -35,6 +35,8 @@ libFiles:
erudaJS: eruda@2.5.0/eruda.js
# fontawesome-free@6.2.0 https://github.com/FortAwesome/Font-Awesome
fontawesomeFreeCSS: '@fortawesome/fontawesome-free@6.2.0/css/all.min.css'
# fusejs@6.6.2 https://github.com/krisk/fuse
fuseJS: fuse.js@6.6.2/dist/fuse.min.js
# gitalk@1.7.2 https://github.com/gitalk/gitalk
gitalkCSS: gitalk@1.7.2/dist/gitalk.min.css
gitalkJS: gitalk@1.7.2/dist/gitalk.min.js
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -10,6 +10,7 @@ echarts@5.3.3 https://github.com/apache/echarts
emoji-data@14.0.0 https://github.com/iamcal/emoji-data
eruda@2.5.0 https://github.com/liriliri/eruda
fontawesome-free@6.2.0 https://github.com/FortAwesome/Font-Awesome
fusejs@6.6.2 https://github.com/krisk/fuse
gitalk@1.7.2 https://github.com/gitalk/gitalk
katex@0.16.2 https://github.com/KaTeX/KaTeX
lazysizes@5.3.2 https://github.com/aFarkas/lazysizes
+9
View File
File diff suppressed because one or more lines are too long
+12 -1
View File
@@ -102,7 +102,7 @@
# FixIt 0.2.0 | NEW Search config
[params.search]
enable = true
# type of search engine ["lunr", "algolia"]
# type of search engine ["lunr", "algolia", "fuse]
type = "lunr"
# max index length of the chunked content
contentLength = 4000
@@ -120,6 +120,17 @@
index = ""
appID = ""
searchKey = ""
[params.search.fuse]
# FixIt 0.2.17 | NEW https://fusejs.io/api/options.html
isCaseSensitive = false
minMatchCharLength = 2
findAllMatches = false
location = 0
threshold = 0.3
distance = 100
ignoreLocation = false
useExtendedSearch = false
ignoreFieldNorm = false
# Header config
[params.header]
+1 -1
Submodule docs updated: 4e650b486a...458ff91768
+7
View File
@@ -28,6 +28,13 @@
{{- $source := $cdn.algoliasearchJS | default "lib/algoliasearch/algoliasearch-lite.umd.min.js" -}}
{{- dict "Source" $source "Fingerprint" $fingerprint "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/script.html" -}}
{{- $config = dict "type" "algolia" "algoliaIndex" $search.algolia.index "algoliaAppID" $search.algolia.appID "algoliaSearchKey" $search.algolia.searchKey | dict "search" | merge $config -}}
{{- else if eq $search.type "fuse" -}}
{{- with .Site.Home.OutputFormats.Get "json" -}}
{{- $config = dict "type" "fuse" "fuseIndexURL" .RelPermalink | dict "search" | merge $config -}}
{{- end -}}
{{- $source := $cdn.fuseJS | default "lib/fuse/fuse.min.js" -}}
{{- dict "Source" $source "Fingerprint" $fingerprint "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/script.html" -}}
{{- $config = dict "isCaseSensitive" $search.fuse.isCaseSensitive "minMatchCharLength" $search.fuse.minMatchCharLength "findAllMatches" $search.fuse.findAllMatches "location" $search.fuse.location "threshold" $search.fuse.threshold "distance" $search.fuse.distance "ignoreLocation" $search.fuse.ignoreLocation "useExtendedSearch" $search.fuse.useExtendedSearch "ignoreFieldNorm" $search.fuse.ignoreFieldNorm | dict "search" | merge $config -}}
{{- end -}}
{{- end -}}
+86 -9
View File
@@ -162,9 +162,19 @@ class FixIt {
if (!searchConfig || (isMobile && this._searchMobileOnce) || (!isMobile && this._searchDesktopOnce)) {
return;
}
const maxResultLength = searchConfig.maxResultLength || 10;
const snippetLength = searchConfig.snippetLength || 50;
const highlightTag = searchConfig.highlightTag || 'em';
// Initialize default search config
const maxResultLength = searchConfig.maxResultLength ?? 10;
const snippetLength = searchConfig.snippetLength ?? 50;
const highlightTag = searchConfig.highlightTag ?? 'em';
const isCaseSensitive = searchConfig.isCaseSensitive ?? false;
const minMatchCharLength = searchConfig.minMatchCharLength ?? 1;
const findAllMatches = searchConfig.findAllMatches ?? false;
const location = searchConfig.location ?? 0;
const threshold = searchConfig.threshold ?? 0.3;
const distance = searchConfig.distance ?? 100;
const ignoreLocation = searchConfig.ignoreLocation ?? false;
const useExtendedSearch = searchConfig.useExtendedSearch ?? false;
const ignoreFieldNorm = searchConfig.ignoreFieldNorm ?? false;
const suffix = isMobile ? 'mobile' : 'desktop';
const $header = document.getElementById(`header-${suffix}`);
const $searchInput = document.getElementById(`search-input-${suffix}`);
@@ -342,7 +352,68 @@ class FixIt {
console.error(err);
finish([]);
});
}
} else if (searchConfig.type === 'fuse') {
const search = () => {
const results = {};
window._index.search(query).forEach(({ item, refIndex, matches }) => {
let title = item.title;
let content = item.content;
matches.forEach(({ indices, value, key }) => {
if (key === 'content') {
let offset = 0;
for (let i = 0; i < indices.length; i++) {
const substr = content.substring(indices[i][0] + offset, indices[i][1] + 1 + offset);
const tag = `<${highlightTag}>` + substr + `</${highlightTag}>`;
content = content.substring(0, indices[i][0] + offset) + tag + content.substring(indices[i][1] + 1 + offset, content.length);
offset += highlightTag.length * 2 + 5;
}
} else if (key === 'title') {
let offset = 0;
for (let i = 0; i < indices.length; i++) {
const substr = title.substring(indices[i][0] + offset, indices[i][1] + 1 + offset);
const tag = `<${highlightTag}>` + substr + `</${highlightTag}>`;
title = title.substring(0, indices[i][0] + offset) + tag + title.substring(indices[i][1] + 1 + offset, content.length);
offset += highlightTag.length * 2 + 5;
}
}
});
results[item.uri] = {
uri: item.uri,
title: title,
date: item.date,
context: content
};
});
return Object.values(results).slice(0, maxResultLength);
};
if (!window._index) {
fetch(searchConfig.fuseIndexURL)
.then((response) => response.json())
.then((data) => {
const options = {
isCaseSensitive: isCaseSensitive,
findAllMatches: findAllMatches,
minMatchCharLength: minMatchCharLength,
location: location,
threshold: threshold,
distance: distance,
ignoreLocation: ignoreLocation,
useExtendedSearch: useExtendedSearch,
ignoreFieldNorm: ignoreFieldNorm,
includeScore: false,
shouldSort: true,
includeMatches: true,
keys: ['content', 'title']
};
window._index = new Fuse(data, options);
finish(search());
})
.catch((err) => {
console.error(err);
finish([]);
});
} else finish(search());
}
},
templates: {
suggestion: ({ title, date, context }) =>
@@ -356,11 +427,17 @@ class FixIt {
icon: '<i class="fa-brands fa-algolia fa-fw" aria-hidden="true"></i>',
href: 'https://www.algolia.com/'
}
: {
searchType: 'Lunr.js',
icon: '',
href: 'https://lunrjs.com/'
};
: (searchConfig.type === 'lunr'
? {
searchType: 'Lunr.js',
icon: '',
href: 'https://lunrjs.com/'
}
: {
searchType: 'Fuse.js',
icon: '',
href: 'https://fusejs.io/'
})
return `<div class="search-footer">Search by <a href="${href}" rel="noopener noreferrer" target="_blank">${icon} ${searchType}</a></div>`;
}
}