Skip to content

Commit 9ddcb23

Browse files
Initial commit for AI Search
1 parent 9499b10 commit 9ddcb23

File tree

13 files changed

+465
-1
lines changed

13 files changed

+465
-1
lines changed

config-geekdoc.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ pluralizeListTitles = false
5151

5252
disableSearch = false
5353

54+
aiSearchEndpoint = "https://docsearch.api.aspose.cloud/ask"
55+
aiSearchScopes = ["viewer_net", "conversion_net"]
56+
5457
# Collapse not active menu items by default
5558
GeekdocCollapseAllSections = true
5659
# Disable default Hugo search
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
"use strict";
2+
3+
const aiSearchConfig = {
4+
searchPromptMinLength: 2,
5+
mdProcessorSettings: {
6+
omitExtraWLInCodeBlocks: true,
7+
noHeaderId: true,
8+
parseImgDimensions: true,
9+
simplifiedAutoLink: true,
10+
excludeTrailingPunctuationFromURLs: true,
11+
literalMidWordUnderscores: true,
12+
literalMidWordAsterisks: true,
13+
strikethrough: true,
14+
tables: true,
15+
tasklists: true,
16+
smoothLivePreview: true,
17+
smartIndentationFix: true,
18+
simpleLineBreaks: true,
19+
encodeEmails: false,
20+
openLinksInNewWindow: true,
21+
backslashEscapesHTMLTags: true
22+
}
23+
};
24+
25+
var aiSearchUI;
26+
var aiSearchMarkdownProcessor;
27+
var aiSearchFingerprint = "";
28+
29+
$(function(){
30+
ThumbmarkJS.getFingerprint().then(function(fp){aiSearchFingerprint=fp;});
31+
// preload DOM
32+
aiSearchUI = {
33+
sidebarField: $("#aiSearchField"),
34+
sidebarButton: $("#aiSearchButton"),
35+
curtain: $("#aiSearchResultsCurtain"),
36+
results: $("#aiSearchResultsForm"),
37+
resultsCloseButton: $("#aiSearchResultsClose"),
38+
resultsField: $("#aiSearchResultsField"),
39+
resultsButton: $("#aiSearchResultsButton"),
40+
preloader: $("#aiSearchResultsPreloader"),
41+
summary: $("#aiSearchResultsSummary"),
42+
relatedLinks: $("#aiSearchResultsForm > aside"),
43+
relatedLinksContainer: $("#aiSearchRelatedArticles"),
44+
linkTemplate: $("#aiSearchRelatedArticleTemplate"),
45+
searchError: $("#aiSearchError")
46+
};
47+
// initialize Markdown converter
48+
aiSearchMarkdownProcessor = new showdown.Converter(aiSearchConfig.mdProcessorSettings);
49+
// event handlers
50+
aiSearchUI.sidebarButton.click(aiSearchOpen);
51+
aiSearchUI.sidebarField.keypress(function(e){if(e.keyCode == 13) aiSearchOpen();});
52+
aiSearchUI.resultsButton.click(aiSearchUpdate);
53+
aiSearchUI.resultsField.keypress(function(e){if(e.keyCode == 13) aiSearchUpdate();});
54+
aiSearchUI.resultsCloseButton.click(aiSearchClose);
55+
$(document).keyup(function(e){if(e.keyCode == 27) aiSearchClose();});
56+
});
57+
58+
function aiSearchReset()
59+
{
60+
aiSearchUI.preloader.show();
61+
aiSearchUI.searchError.hide();
62+
aiSearchUI.summary.html("");
63+
aiSearchUI.relatedLinks.hide();
64+
$("#aiSearchRelatedArticles > a:not(#aiSearchRelatedArticleTemplate)").remove();
65+
}
66+
67+
function aiSearchOpen()
68+
{
69+
let prompt = aiSearchUI.sidebarField.val().trim();
70+
if(prompt.length < aiSearchConfig.searchPromptMinLength)
71+
{
72+
console.log(`Search query is less than ${aiSearchConfig.searchPromptMinLength} characters`);
73+
return false;
74+
}
75+
// show search results interface
76+
aiSearchReset();
77+
aiSearchUI.resultsField.val(prompt);
78+
aiSearchUI.curtain.removeClass("ai-search-invisible");
79+
aiSearchUI.results.removeClass("ai-search-invisible");
80+
// send search request
81+
aiSearchRequest(prompt);
82+
}
83+
84+
function aiSearchUpdate()
85+
{
86+
let prompt = aiSearchUI.resultsField.val().trim();
87+
if(prompt.length < aiSearchConfig.searchPromptMinLength)
88+
{
89+
console.log(`Search query is less than ${aiSearchConfig.searchPromptMinLength} characters`);
90+
return false;
91+
}
92+
aiSearchReset();
93+
// send search request
94+
aiSearchRequest(prompt);
95+
}
96+
97+
function aiSearchClose()
98+
{
99+
aiSearchUI.curtain.addClass("ai-search-invisible");
100+
aiSearchUI.results.addClass("ai-search-invisible");
101+
}
102+
103+
function aiSearchRequest(prompt)
104+
{
105+
let request = {
106+
query: prompt,
107+
scope: aiSearchScope,
108+
referrer: `${window.location.protocol}//${window.location.host}${window.location.pathname}`,
109+
fingerprint: aiSearchFingerprint
110+
};
111+
$.ajax({
112+
method: "POST",
113+
contentType: "application/json",
114+
url: aiSearchEndpoint,
115+
data: JSON.stringify(request)
116+
})
117+
.done(aiSearchParseResults)
118+
.fail(aiSearchShowError)
119+
.always(function(){aiSearchUI.preloader.hide();});
120+
}
121+
122+
function aiSearchParseResults(data)
123+
{
124+
let summary = aiSearchMarkdownProcessor.makeHtml(data.result.summary);
125+
aiSearchUI.summary.html(summary);
126+
if(data.result.articles.length>0)
127+
{
128+
for(var i=0;i<data.result.articles.length;i++)
129+
{
130+
let item = data.result.articles[i];
131+
let fullUrl = item.root;
132+
fullUrl += (item.url[0] == "/")?item.url.substring(1):item.url;
133+
let placeholder = aiSearchUI.linkTemplate.clone();
134+
placeholder
135+
.removeAttr("id")
136+
.attr("href", fullUrl)
137+
.text(item.title)
138+
.attr("title", item.description)
139+
.appendTo(aiSearchUI.relatedLinksContainer);
140+
}
141+
aiSearchUI.relatedLinks.show();
142+
}
143+
}
144+
145+
function aiSearchShowError(jqXHR,textStatus,error)
146+
{
147+
aiSearchUI.searchError.show();
148+
console.log(`Error ${jqXHR.status}: ${jqXHR.responseText}`);
149+
}

themes/hugo-geekdoc/assets/js/showdown.min.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

themes/hugo-geekdoc/assets/js/thumbmark.umd.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// AI-powered search
2+
3+
.ai-search-invisible {
4+
display: none !important;
5+
}
6+
7+
.ai-search-form {
8+
display: flex;
9+
align-items: center;
10+
height: 30px;
11+
margin-bottom: 10px;
12+
padding: 0px 2px 0px 15px;
13+
border: solid 1px #cccccc;
14+
border-radius: 15px;
15+
background-color: #ffffff;
16+
17+
&:hover {
18+
border-color: #1a89d0;
19+
}
20+
21+
#aiSearchField {
22+
flex-grow: 1;
23+
margin-right: 7px;
24+
border: none;
25+
outline: none;
26+
font-family: $font-family-base;
27+
color: #222222;
28+
background-color: transparent;
29+
}
30+
31+
#aiSearchButton {
32+
height: 24px;
33+
padding: 0px 18px;
34+
border-radius: 13px;
35+
border: none;
36+
outline: none;
37+
white-space: nowrap;
38+
background-color: #1a89d0;
39+
font-family: $font-family-base;
40+
color: #ffffff;
41+
cursor: pointer;
42+
43+
&:hover {
44+
background-color: #3071a9;
45+
}
46+
}
47+
}
48+
49+
#aiSearchResultsCurtain {
50+
z-index: 99;
51+
position: fixed;
52+
top: 0px;
53+
left: 0px;
54+
bottom: 0px;
55+
right: 0px;
56+
background-color: #000000;
57+
opacity: 0.7;
58+
}
59+
60+
#aiSearchResultsForm {
61+
display: flex;
62+
flex-direction: column;
63+
z-index: 100;
64+
position: fixed;
65+
top: 10%;
66+
left: 10%;
67+
bottom: 10%;
68+
right: 10%;
69+
padding: 15px;
70+
border: solid 1px #cccccc;
71+
box-shadow: 0px 0px 5px 3px rgba(68,68,68,1);
72+
background-color: #ffffff;
73+
74+
& > header {
75+
display: flex;
76+
align-items: center;
77+
margin-bottom: 20px;
78+
79+
.ai-search-results-prompt {
80+
display: flex;
81+
flex-grow: 1;
82+
align-items: center;
83+
height: 40px;
84+
padding: 0px 2px 0px 20px;
85+
border: solid 1px #cccccc;
86+
border-radius: 20px;
87+
background-color: #ffffff;
88+
font-size: 16px;
89+
90+
#aiSearchResultsField {
91+
flex-grow: 1;
92+
margin-right: 7px;
93+
border: none;
94+
outline: none;
95+
font-family: $font-family-base;
96+
color: #222222;
97+
background-color: transparent;
98+
}
99+
100+
#aiSearchResultsButton {
101+
height: 34px;
102+
padding: 0px 18px;
103+
border-radius: 17px;
104+
border: none;
105+
outline: none;
106+
white-space: nowrap;
107+
background-color: #1a89d0;
108+
font-family: $font-family-base;
109+
font-weight: 600;
110+
color: #ffffff;
111+
cursor: pointer;
112+
113+
&:hover {
114+
background-color: #3071a9;
115+
}
116+
}
117+
}
118+
119+
.ai-search-results-tools {
120+
margin-left: 30px;
121+
122+
& > img {
123+
width: auto;
124+
height: 34px;
125+
cursor: pointer;
126+
opacity: 0.3;
127+
128+
&:hover {
129+
opacity: 0.9;
130+
}
131+
}
132+
}
133+
}
134+
135+
& > article {
136+
flex-grow: 1;
137+
overflow: auto;
138+
padding: 10px 20px;
139+
140+
#aiSearchResultsPreloader {
141+
margin-top: 15px;
142+
text-align: center;
143+
144+
& > img {
145+
margin-bottom: 15px;
146+
}
147+
148+
& > p {
149+
color: #666666;
150+
font-style: italic;
151+
}
152+
}
153+
154+
#aiSearchError {
155+
display: none;
156+
font-size: 16px;
157+
font-weight: 500;
158+
color: #aa0000;
159+
}
160+
161+
pre {
162+
padding: 10px;
163+
border: solid 1px #c2c2c2;
164+
background-color: #f8f9fa;
165+
}
166+
}
167+
168+
& > aside {
169+
display: none;
170+
margin-top: 20px;
171+
padding: 10px 20px;
172+
border-top: solid 1px #cccccc;
173+
174+
.ai-search-results-title {
175+
margin-top: 5px;
176+
margin-bottom: 15px;
177+
font-weight: 500;
178+
font-size: 20px;
179+
}
180+
181+
#aiSearchRelatedArticles {
182+
display: flex;
183+
flex-wrap: wrap;
184+
align-items: top;
185+
186+
#aiSearchRelatedArticleTemplate {
187+
display: none;
188+
}
189+
190+
& > a {
191+
display: block;
192+
min-width: 200px;
193+
margin: 0px 15px 5px 0px;
194+
padding: 10px 15px;
195+
border-radius: 10px;
196+
box-shadow: inset 0px 0px 5px 0px rgba(0,0,0,0.2);
197+
text-decoration: none;
198+
white-space: nowrap;
199+
overflow: hidden;
200+
201+
&:hover {
202+
text-decoration: none;
203+
box-shadow: inset 0px 0px 5px 0px rgba(26,137,208,1);;
204+
}
205+
}
206+
}
207+
}
208+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Fonts
2+
3+
$google_font_name: "Arial" !default;
4+
$google_font_family: "Open+Sans:300,300i,400,400i,700,700i" !default;
5+
$web-font-path: "https://fonts.googleapis.com/css?family=#{$google_font_family}";
6+
7+
$td-fonts-serif: sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
8+
9+
// @if $td-enable-google-fonts {
10+
// $td-fonts-serif: prepend($td-fonts-serif, "#{$google_font_name}");
11+
// }
12+
13+
$font-family-sans-serif: $td-fonts-serif !default;
14+
15+
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
16+
$font-family-base: $font-family-sans-serif !default;
17+
$font-size-base: 0.875rem !default;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@import "variables";
2+
@import "ai-search";
3+

0 commit comments

Comments
 (0)