initial impl of search
continuous-integration/drone/push Build is passing Details

This commit is contained in:
James Ravenscroft 2022-01-18 21:21:41 +00:00
parent 04f7b0bd4f
commit 26e9a4487e
8 changed files with 206 additions and 1 deletions

View File

@ -17,6 +17,9 @@ webMentionAPIKey = "f61bf-RG1k4uZT3fVLDoIw"
googleAnalytics = "UA-186263385-1"
[outputs]
home = ["HTML", "RSS", "JSON"]
[permalinks]
'/' = '/:year/:month/:filename/'

View File

@ -0,0 +1,16 @@
---
title: Wakapi Time Tracking
type: post
description: Playing with Wakapi - a time tracking server that plays nicely with wakatime
resources:
- name: feature
src: images/wedding.jpg
date: 2022-01-08T09:05:11+00:00
url: /2022/01/08/wakapi-time-tracking
tags:
- open-source
---
This week I spotted a social media post about [wakapi](https://wakapi.dev/) which is an open source implementation of the [WakaTime](https://wakatime.com/) time tracking server software (which up until that point I hadn't heard of). The thing that interests me about these solutios that they're kind of developer focussed and tell you things like which project you had open, what programming language(s) you spent most time reading/writing and so on. Wakatime also have nice easy install-and-forget plugins for a [BUNCH of IDEs](https://wakatime.com/plugins) including VSCode (where I spend most of my professional working day), NeoVIM, PyCharm and others. All of the plugins have open source (generally seems to be BSD 3-clause) licensing.
I'm quite interested in tracking where my time is being spent - both in my personal hobbyist/phd dev activities and also

View File

@ -0,0 +1,52 @@
---
title: "Search Results"
sitemap:
priority : 0.1
layout: "search"
url: /search
---
This file exists solely to respond to /search URL with the related `search` layout template.
No content shown here is rendered, all content is based in the template layouts/page/search.html
Setting a very low sitemap priority will tell search engines this is not important content.
This implementation uses Fusejs, jquery and mark.js
## Initial setup
Search depends on additional output content type of JSON in config.toml
\```
[outputs]
home = ["HTML", "JSON"]
\```
## Searching additional fileds
To search additional fields defined in front matter, you must add it in 2 places.
### Edit layouts/_default/index.JSON
This exposes the values in /index.json
i.e. add `category`
\```
...
"contents":{{ .Content | plainify | jsonify }}
{{ if .Params.tags }},
"tags":{{ .Params.tags | jsonify }}{{end}},
"categories" : {{ .Params.categories | jsonify }},
...
\```
### Edit fuse.js options to Search
`static/js/search.js`
\```
keys: [
"title",
"contents",
"tags",
"categories"
]
\```

9
brainsteam/static/js/fuse.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

13
brainsteam/static/js/jquery.mark.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,110 @@
summaryInclude=60;
var fuseOptions = {
shouldSort: true,
includeMatches: true,
threshold: 0.0,
tokenize:true,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
{name:"title",weight:0.8},
{name:"contents",weight:0.5},
{name:"tags",weight:0.3},
{name:"categories",weight:0.3}
]
};
var searchQuery = param("s");
if(searchQuery){
$("#search-query").val(searchQuery);
executeSearch(searchQuery);
}else {
$('#search-results').append("<p>Please enter a word or phrase above</p>");
}
function executeSearch(searchQuery){
$.getJSON( "/index.json", function( data ) {
var pages = data;
var fuse = new Fuse(pages, fuseOptions);
var result = fuse.search(searchQuery);
console.log({"matches":result});
if(result.length > 0){
populateResults(result);
}else{
$('#search-results').append("<p>No matches found</p>");
}
});
}
function populateResults(result){
$.each(result,function(key,value){
var contents= value.item.contents;
var snippet = "";
var snippetHighlights=[];
var tags =[];
if( fuseOptions.tokenize ){
snippetHighlights.push(searchQuery);
}else{
$.each(value.matches,function(matchKey,mvalue){
if(mvalue.key == "tags" || mvalue.key == "categories" ){
snippetHighlights.push(mvalue.value);
}else if(mvalue.key == "contents"){
start = mvalue.indices[0][0]-summaryInclude>0?mvalue.indices[0][0]-summaryInclude:0;
end = mvalue.indices[0][1]+summaryInclude<contents.length?mvalue.indices[0][1]+summaryInclude:contents.length;
snippet += contents.substring(start,end);
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0],mvalue.indices[0][1]-mvalue.indices[0][0]+1));
}
});
}
if(snippet.length<1){
snippet += contents.substring(0,summaryInclude*2);
}
//pull template from hugo templarte definition
var templateDefinition = $('#search-result-template').html();
//replace values
var output = render(templateDefinition,{key:key,title:value.item.title,link:value.item.permalink,tags:value.item.tags,categories:value.item.categories,snippet:snippet});
$('#search-results').append(output);
$.each(snippetHighlights,function(snipkey,snipvalue){
$("#summary-"+key).mark(snipvalue);
});
});
}
function param(name) {
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
}
function render(templateString, data) {
var conditionalMatches,conditionalPattern,copy;
conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
//since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
copy = templateString;
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
if(data[conditionalMatches[1]]){
//valid key, remove conditionals, leave contents.
copy = copy.replace(conditionalMatches[0],conditionalMatches[2]);
}else{
//not valid, remove entire section
copy = copy.replace(conditionalMatches[0],'');
}
}
templateString = copy;
//now any conditionals removed we can do simple substitution
var key, find, re;
for (key in data) {
find = '\\$\\{\\s*' + key + '\\s*\\}';
re = new RegExp(find, 'g');
templateString = templateString.replace(re, data[key]);
}
return templateString;
}

@ -1 +1 @@
Subproject commit e59b107a3b7bbf56dbf9ad79a85bf2cbde15df50
Subproject commit 45aaa750312fed860420987610d2c3e82018ce85