jekyll-client-search 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/README.developer.md +272 -10
- data/README.md +224 -4
- data/assets/client-search-base.js +53 -1
- data/assets/client-search-dropdown.js +434 -0
- data/assets/client-search-related.js +10 -0
- data/lib/jekyll/client_search/configuration.rb +40 -13
- data/lib/jekyll/client_search/configuration_accessors.rb +12 -4
- data/lib/jekyll/client_search/document_builder.rb +23 -5
- data/lib/jekyll/client_search/dropdown_configuration.rb +92 -0
- data/lib/jekyll/client_search/dropdown_tag.rb +115 -0
- data/lib/jekyll/client_search/generator.rb +6 -2
- data/lib/jekyll/client_search/related_configuration.rb +1 -1
- data/lib/jekyll/client_search/related_tag.rb +30 -8
- data/lib/jekyll/client_search/runtime_config_page.rb +5 -1
- data/lib/jekyll/client_search/search_tag.rb +23 -6
- data/lib/jekyll/client_search/version.rb +1 -1
- data/lib/jekyll/client_search.rb +2 -0
- metadata +4 -1
|
@@ -5,11 +5,21 @@ module Jekyll
|
|
|
5
5
|
# Normalizes Jekyll documents and pages into the flat hash shape that the
|
|
6
6
|
# search index JSON emits.
|
|
7
7
|
class DocumentBuilder
|
|
8
|
-
def from_document(document)
|
|
8
|
+
def from_document(document, source: nil, passthrough_fields: [])
|
|
9
9
|
url = document.url.to_s
|
|
10
10
|
return if url.empty?
|
|
11
11
|
|
|
12
12
|
data = document.data
|
|
13
|
+
core_fields(document, data, url)
|
|
14
|
+
.compact
|
|
15
|
+
.merge("source" => source)
|
|
16
|
+
.merge(passthrough(data, passthrough_fields))
|
|
17
|
+
.compact
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def core_fields(document, data, url)
|
|
13
23
|
{
|
|
14
24
|
"id" => url,
|
|
15
25
|
"title" => clean(data["title"] || "Untitled"),
|
|
@@ -20,10 +30,18 @@ module Jekyll
|
|
|
20
30
|
"tags" => normalize_list(data["tags"]),
|
|
21
31
|
"date" => normalized_date(document),
|
|
22
32
|
"date_timestamp" => normalized_timestamp(document)
|
|
23
|
-
}
|
|
33
|
+
}
|
|
24
34
|
end
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
def passthrough(data, fields)
|
|
37
|
+
fields.each_with_object({}) do |(source, target), result|
|
|
38
|
+
value = data[source]
|
|
39
|
+
next if value.nil?
|
|
40
|
+
next if value.is_a?(String) && value.empty?
|
|
41
|
+
|
|
42
|
+
result[target] = value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
27
45
|
|
|
28
46
|
def excerpt_for(document)
|
|
29
47
|
document.excerpt if document.respond_to?(:excerpt)
|
|
@@ -49,8 +67,8 @@ module Jekyll
|
|
|
49
67
|
|
|
50
68
|
def clean(value)
|
|
51
69
|
cleaned = value.to_s
|
|
52
|
-
.gsub(%r{<script\b[^>]*>.*?</script
|
|
53
|
-
.gsub(%r{<style\b[^>]*>.*?</style
|
|
70
|
+
.gsub(%r{<script\b[^>]*>.*?</script\s*>}mi, " ")
|
|
71
|
+
.gsub(%r{<style\b[^>]*>.*?</style\s*>}mi, " ")
|
|
54
72
|
.gsub(/\{%.*?%\}/m, " ")
|
|
55
73
|
.gsub(/\{\{.*?\}\}/m, " ")
|
|
56
74
|
.gsub(/!\[[^\]]*\]\([^)]*\)/, " ")
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module ClientSearch
|
|
7
|
+
# Validates optional compact dropdown live-search settings.
|
|
8
|
+
class DropdownConfiguration
|
|
9
|
+
DEFAULTS = {
|
|
10
|
+
"enabled" => true,
|
|
11
|
+
"max_items" => 5,
|
|
12
|
+
"min_chars" => 2,
|
|
13
|
+
"debounce_ms" => 150,
|
|
14
|
+
"redirect_url" => "/search/"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def initialize(config)
|
|
18
|
+
unless config.is_a?(Hash)
|
|
19
|
+
raise Jekyll::Errors::FatalException,
|
|
20
|
+
"client_search dropdown configuration must be a mapping"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@values = DEFAULTS.merge(config)
|
|
24
|
+
validate!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def enabled?
|
|
28
|
+
@values.fetch("enabled") != false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def max_items
|
|
32
|
+
@values.fetch("max_items")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def min_chars
|
|
36
|
+
@values.fetch("min_chars")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def debounce_ms
|
|
40
|
+
@values.fetch("debounce_ms")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def redirect_url
|
|
44
|
+
@values.fetch("redirect_url").to_s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_h
|
|
48
|
+
{
|
|
49
|
+
"enabled" => enabled?,
|
|
50
|
+
"maxItems" => max_items,
|
|
51
|
+
"minChars" => min_chars,
|
|
52
|
+
"debounceMs" => debounce_ms,
|
|
53
|
+
"redirectUrl" => redirect_url
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def validate!
|
|
60
|
+
validate_boolean!("enabled")
|
|
61
|
+
validate_integer!("max_items", minimum: 1)
|
|
62
|
+
validate_integer!("min_chars", minimum: 0)
|
|
63
|
+
validate_integer!("debounce_ms", minimum: 0)
|
|
64
|
+
if redirect_url.empty?
|
|
65
|
+
raise Jekyll::Errors::FatalException,
|
|
66
|
+
"client_search dropdown redirect_url must not be empty"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
uri = URI.parse(redirect_url)
|
|
70
|
+
return if uri.scheme.nil? && uri.host.nil? && !redirect_url.start_with?("//")
|
|
71
|
+
|
|
72
|
+
raise Jekyll::Errors::FatalException,
|
|
73
|
+
"client_search dropdown redirect_url must be a relative path"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def validate_boolean!(key)
|
|
77
|
+
return if [true, false].include?(@values[key])
|
|
78
|
+
|
|
79
|
+
raise Jekyll::Errors::FatalException,
|
|
80
|
+
"client_search dropdown.#{key} must be true or false"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def validate_integer!(key, minimum:)
|
|
84
|
+
value = @values.fetch(key)
|
|
85
|
+
return if value.is_a?(Integer) && value >= minimum
|
|
86
|
+
|
|
87
|
+
raise Jekyll::Errors::FatalException,
|
|
88
|
+
"client_search dropdown.#{key} must be an integer greater than or equal to #{minimum}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module ClientSearch
|
|
7
|
+
# Liquid tag that renders a compact live-search dropdown suitable for
|
|
8
|
+
# navbars and headers. Framework-agnostic — emits semantic HTML with
|
|
9
|
+
# data attributes, no CSS classes from any framework.
|
|
10
|
+
#
|
|
11
|
+
# {% search_dropdown %}
|
|
12
|
+
# {% search_dropdown max:10 %}
|
|
13
|
+
# {% search_dropdown scripts_only %}
|
|
14
|
+
# {% search_dropdown no_scripts %}
|
|
15
|
+
#
|
|
16
|
+
# When client_search is disabled the tag renders nothing.
|
|
17
|
+
class DropdownTag < Liquid::Tag
|
|
18
|
+
SYNTAX = /\A(max:(\d+))?\s*(scripts_only|no_scripts)?\z/
|
|
19
|
+
|
|
20
|
+
def initialize(tag_name, markup, tokens)
|
|
21
|
+
super
|
|
22
|
+
@markup = markup.to_s.strip
|
|
23
|
+
unless (match = @markup.match(SYNTAX))
|
|
24
|
+
raise Liquid::SyntaxError,
|
|
25
|
+
"search_dropdown: invalid syntax. Use {% search_dropdown %}, " \
|
|
26
|
+
"{% search_dropdown max:10 %}, {% search_dropdown scripts_only %}, " \
|
|
27
|
+
"or {% search_dropdown no_scripts %}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@max_items = match[2].to_i if match[2]
|
|
31
|
+
raise Liquid::SyntaxError, "search_dropdown: max must be greater than zero" if @max_items && @max_items < 1
|
|
32
|
+
|
|
33
|
+
@mode = match[3] || "full"
|
|
34
|
+
@input_id = "cs-dropdown-input-#{object_id}"
|
|
35
|
+
@results_id = "cs-dropdown-results-#{object_id}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render(context)
|
|
39
|
+
site = context.registers[:site]
|
|
40
|
+
return "" unless enabled?(site)
|
|
41
|
+
|
|
42
|
+
configuration = Configuration.new(site)
|
|
43
|
+
return "" unless configuration.dropdown_enabled?
|
|
44
|
+
|
|
45
|
+
render_mode(configuration, site)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def enabled?(site)
|
|
51
|
+
return false if site.nil?
|
|
52
|
+
|
|
53
|
+
config_hash = site.config.fetch("client_search", {})
|
|
54
|
+
config_hash != false && config_hash["enabled"] != false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def render_mode(configuration, site)
|
|
58
|
+
baseurl = site.config["baseurl"].to_s.gsub(%r{\A/+|/+$}, "")
|
|
59
|
+
prefix = baseurl.empty? ? "" : "/#{baseurl}"
|
|
60
|
+
max = resolve_max(configuration)
|
|
61
|
+
|
|
62
|
+
form_html = build_form(max)
|
|
63
|
+
return form_html if @mode == "no_scripts"
|
|
64
|
+
|
|
65
|
+
scripts = build_scripts(configuration, prefix)
|
|
66
|
+
return scripts if @mode == "scripts_only"
|
|
67
|
+
|
|
68
|
+
"#{form_html}\n#{scripts}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def resolve_max(configuration)
|
|
72
|
+
@max_items || configuration.dropdown_max_items
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def build_form(max)
|
|
76
|
+
<<~HTML
|
|
77
|
+
<div class="client-search-dropdown" data-client-search-dropdown>
|
|
78
|
+
<form role="search" autocomplete="off" data-cs-dropdown-form>
|
|
79
|
+
<label class="sr-only" for="#{@input_id}">Search</label>
|
|
80
|
+
<input id="#{@input_id}" type="search" name="q"
|
|
81
|
+
placeholder="Search…" autocomplete="off"
|
|
82
|
+
aria-expanded="false" aria-controls="#{@results_id}"
|
|
83
|
+
data-cs-dropdown-input>
|
|
84
|
+
</form>
|
|
85
|
+
<ul id="#{@results_id}" role="listbox" aria-hidden="true"
|
|
86
|
+
data-cs-dropdown-results data-max-items="#{max}"></ul>
|
|
87
|
+
</div>
|
|
88
|
+
HTML
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def build_scripts(configuration, prefix)
|
|
92
|
+
scripts = []
|
|
93
|
+
engine = configuration.engine_url
|
|
94
|
+
if engine
|
|
95
|
+
attrs = ["src=\"#{CGI.escapeHTML(engine)}\""]
|
|
96
|
+
if configuration.engine_crossorigin
|
|
97
|
+
crossorigin = CGI.escapeHTML(configuration.engine_crossorigin)
|
|
98
|
+
attrs << "crossorigin=\"#{crossorigin}\""
|
|
99
|
+
end
|
|
100
|
+
if configuration.engine_sri
|
|
101
|
+
integrity = CGI.escapeHTML(configuration.engine_sri)
|
|
102
|
+
attrs << "integrity=\"#{integrity}\""
|
|
103
|
+
end
|
|
104
|
+
scripts << "<script #{attrs.join(' ')}></script>"
|
|
105
|
+
end
|
|
106
|
+
scripts << "<script src=\"#{prefix}/assets/search-runtime-config.js\"></script>"
|
|
107
|
+
scripts << "<script src=\"#{prefix}/assets/client-search-dropdown.js\"></script>"
|
|
108
|
+
scripts << "<script src=\"#{prefix}/assets/adapters/#{configuration.engine}.js\"></script>"
|
|
109
|
+
scripts.map { |script| " #{script}" }.join("\n")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Liquid::Template.register_tag("search_dropdown", Jekyll::ClientSearch::DropdownTag)
|
|
@@ -90,19 +90,23 @@ module Jekyll
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def collection_documents(site, configuration, builder)
|
|
93
|
+
fields = configuration.passthrough_fields
|
|
93
94
|
configuration.collections.flat_map do |label|
|
|
94
95
|
collection = label == "posts" ? site.posts : site.collections[label]
|
|
95
96
|
next [] unless collection
|
|
96
97
|
|
|
97
|
-
collection.docs.filter_map
|
|
98
|
+
collection.docs.filter_map do |document|
|
|
99
|
+
builder.from_document(document, source: label, passthrough_fields: fields)
|
|
100
|
+
end
|
|
98
101
|
end
|
|
99
102
|
end
|
|
100
103
|
|
|
101
104
|
def page_documents(site, builder, configuration)
|
|
105
|
+
fields = configuration.passthrough_fields
|
|
102
106
|
site.pages
|
|
103
107
|
.reject { |page| page.url == "/#{configuration.output}" }
|
|
104
108
|
.select { |page| page.data["title"] }
|
|
105
|
-
.filter_map { |page| builder.from_document(page) }
|
|
109
|
+
.filter_map { |page| builder.from_document(page, source: "pages", passthrough_fields: fields) }
|
|
106
110
|
end
|
|
107
111
|
|
|
108
112
|
def add_runtime_asset(site, configuration)
|
|
@@ -7,35 +7,45 @@ module Jekyll
|
|
|
7
7
|
#
|
|
8
8
|
# {% related_articles %}
|
|
9
9
|
# {% related_articles sort:date %}
|
|
10
|
+
# {% related_articles max:3 %}
|
|
11
|
+
# {% related_articles sort:date max:3 %}
|
|
10
12
|
# {% related_articles no_scripts %}
|
|
11
13
|
#
|
|
12
14
|
# When +related.enabled+ is false the tag renders nothing, so it is safe
|
|
13
15
|
# to leave in a layout even when the feature is off.
|
|
14
16
|
class RelatedTag < Liquid::Tag
|
|
15
|
-
SYNTAX = /\A\s*(
|
|
17
|
+
SYNTAX = /\A(sort:(\w+))?\s*(max:(\d+))?\s*(no_scripts)?\z/
|
|
16
18
|
|
|
17
19
|
def initialize(tag_name, markup, tokens)
|
|
18
20
|
super
|
|
19
|
-
@markup = markup.to_s
|
|
21
|
+
@markup = markup.to_s.strip
|
|
20
22
|
unless (match = @markup.match(SYNTAX))
|
|
21
23
|
raise Liquid::SyntaxError,
|
|
22
24
|
"related_articles: invalid syntax. Use {% related_articles %}, " \
|
|
23
|
-
"{% related_articles sort:date %},
|
|
25
|
+
"{% related_articles sort:date %}, {% related_articles max:3 %}, " \
|
|
26
|
+
"or {% related_articles no_scripts %}"
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
@sort = match[2] if match[2]
|
|
27
|
-
@
|
|
30
|
+
@max_items = match[4].to_i if match[4]
|
|
31
|
+
raise Liquid::SyntaxError, "related_articles: max must be greater than zero" if @max_items && @max_items < 1
|
|
32
|
+
|
|
33
|
+
@include_scripts = match[5].nil?
|
|
28
34
|
end
|
|
29
35
|
|
|
30
36
|
def render(context)
|
|
31
37
|
site = context.registers[:site]
|
|
32
|
-
|
|
38
|
+
return "" if site.nil?
|
|
39
|
+
|
|
40
|
+
config = site.config.fetch("client_search", {})
|
|
41
|
+
return "" if config == false
|
|
33
42
|
return "" unless related_enabled?(config)
|
|
34
43
|
|
|
35
44
|
asset_prefix = asset_prefix(site)
|
|
36
45
|
sort_attr = @sort ? " data-related-sort=\"#{@sort}\"" : ""
|
|
46
|
+
max_attr = resolve_max_attr(config)
|
|
37
47
|
scripts = build_scripts(asset_prefix)
|
|
38
|
-
build_html(sort_attr, scripts)
|
|
48
|
+
build_html(sort_attr, max_attr, scripts)
|
|
39
49
|
end
|
|
40
50
|
|
|
41
51
|
private
|
|
@@ -57,7 +67,19 @@ module Jekyll
|
|
|
57
67
|
"<script src=\"#{prefix}/assets/client-search-related.js\"></script>"
|
|
58
68
|
end
|
|
59
69
|
|
|
60
|
-
def
|
|
70
|
+
def resolve_max_attr(config)
|
|
71
|
+
related = config.fetch("related", {})
|
|
72
|
+
max = if @max_items
|
|
73
|
+
@max_items
|
|
74
|
+
elsif related.key?("max_items")
|
|
75
|
+
related["max_items"]
|
|
76
|
+
else
|
|
77
|
+
5
|
|
78
|
+
end
|
|
79
|
+
max.nil? ? "" : " data-related-max=\"#{max}\""
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_html(sort_attr, max_attr, scripts)
|
|
61
83
|
<<~HTML
|
|
62
84
|
<section class="related-articles-section">
|
|
63
85
|
<label for="related-sort">Sort related articles</label>
|
|
@@ -65,7 +87,7 @@ module Jekyll
|
|
|
65
87
|
<option value="relevance">Most related</option>
|
|
66
88
|
<option value="date">Newest</option>
|
|
67
89
|
</select>
|
|
68
|
-
<div id="related-articles"#{sort_attr}></div>
|
|
90
|
+
<div id="related-articles"#{sort_attr}#{max_attr}></div>
|
|
69
91
|
</section>#{scripts}
|
|
70
92
|
HTML
|
|
71
93
|
end
|
|
@@ -12,10 +12,14 @@ module Jekyll
|
|
|
12
12
|
"liveSearch" => configuration.live_search_config
|
|
13
13
|
}
|
|
14
14
|
defaults["relatedUrl"] = index_url(site, configuration.related_output) if configuration.related_enabled?
|
|
15
|
+
defaults["iconField"] = configuration.runtime_icon_field if configuration.runtime_icon_field
|
|
16
|
+
defaults["dropdown"] = configuration.dropdown_config if configuration.dropdown_enabled?
|
|
15
17
|
json = JSON.generate(defaults)
|
|
16
18
|
self.content = "window.clientSearchConfig = (function (generated, existing) {" \
|
|
17
19
|
"var liveSearch = Object.assign({}, generated.liveSearch, existing.liveSearch || {});" \
|
|
18
|
-
"
|
|
20
|
+
"var dropdown = Object.assign({}, generated.dropdown, existing.dropdown || {});" \
|
|
21
|
+
"return Object.assign(generated, existing, " \
|
|
22
|
+
"{ liveSearch: liveSearch, dropdown: dropdown });" \
|
|
19
23
|
"}(#{json}, window.clientSearchConfig || {}));\n"
|
|
20
24
|
end
|
|
21
25
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "cgi"
|
|
4
|
+
|
|
3
5
|
module Jekyll
|
|
4
6
|
module ClientSearch
|
|
5
7
|
# Liquid tag that renders the search form, status/results containers,
|
|
@@ -12,11 +14,11 @@ module Jekyll
|
|
|
12
14
|
#
|
|
13
15
|
# When client_search is disabled the tag renders nothing.
|
|
14
16
|
class SearchTag < Liquid::Tag
|
|
15
|
-
SYNTAX = /\A
|
|
17
|
+
SYNTAX = /\A(scripts_only|no_scripts)?\z/
|
|
16
18
|
|
|
17
19
|
def initialize(tag_name, markup, tokens)
|
|
18
20
|
super
|
|
19
|
-
@markup = markup.to_s
|
|
21
|
+
@markup = markup.to_s.strip
|
|
20
22
|
unless (match = @markup.match(SYNTAX))
|
|
21
23
|
raise Liquid::SyntaxError,
|
|
22
24
|
"search_form: invalid syntax. Use {% search_form %}, " \
|
|
@@ -28,7 +30,10 @@ module Jekyll
|
|
|
28
30
|
|
|
29
31
|
def render(context)
|
|
30
32
|
site = context.registers[:site]
|
|
31
|
-
|
|
33
|
+
return "" if site.nil?
|
|
34
|
+
|
|
35
|
+
config_hash = site.config.fetch("client_search", {})
|
|
36
|
+
return "" if config_hash == false
|
|
32
37
|
return "" unless config_hash["enabled"] != false
|
|
33
38
|
|
|
34
39
|
configuration = Configuration.new(site)
|
|
@@ -73,9 +78,15 @@ module Jekyll
|
|
|
73
78
|
url = configuration.engine_url
|
|
74
79
|
return nil unless url
|
|
75
80
|
|
|
76
|
-
attrs = ["src=\"#{url}\""]
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
attrs = ["src=\"#{CGI.escapeHTML(url)}\""]
|
|
82
|
+
if configuration.engine_crossorigin
|
|
83
|
+
crossorigin = CGI.escapeHTML(configuration.engine_crossorigin)
|
|
84
|
+
attrs << "crossorigin=\"#{crossorigin}\""
|
|
85
|
+
end
|
|
86
|
+
if configuration.engine_sri
|
|
87
|
+
integrity = CGI.escapeHTML(configuration.engine_sri)
|
|
88
|
+
attrs << "integrity=\"#{integrity}\""
|
|
89
|
+
end
|
|
79
90
|
"<script #{attrs.join(' ')}></script>"
|
|
80
91
|
end
|
|
81
92
|
|
|
@@ -84,7 +95,13 @@ module Jekyll
|
|
|
84
95
|
|
|
85
96
|
scripts = ["<script src=\"#{prefix}/assets/search-embedder-config.js\"></script>"]
|
|
86
97
|
embedder_asset = configuration.query_embedder_asset
|
|
98
|
+
# simplecov:disable branch
|
|
99
|
+
# Defensive guard: semantic_with_embedder? already excludes the "none"
|
|
100
|
+
# type, and both "transformers" and "ollama_api" always return an asset.
|
|
101
|
+
# This branch is unreachable with current embedder types but protects
|
|
102
|
+
# against future types that may have no script asset.
|
|
87
103
|
scripts << "<script src=\"#{prefix}/#{embedder_asset}\"></script>" if embedder_asset
|
|
104
|
+
# simplecov:enable branch
|
|
88
105
|
scripts
|
|
89
106
|
end
|
|
90
107
|
|
data/lib/jekyll/client_search.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative "client_search/version"
|
|
|
9
9
|
require_relative "client_search/embedding_configuration"
|
|
10
10
|
require_relative "client_search/live_search_configuration"
|
|
11
11
|
require_relative "client_search/related_configuration"
|
|
12
|
+
require_relative "client_search/dropdown_configuration"
|
|
12
13
|
require_relative "client_search/query_embedder_configuration"
|
|
13
14
|
require_relative "client_search/configuration_accessors"
|
|
14
15
|
require_relative "client_search/configuration"
|
|
@@ -22,4 +23,5 @@ require_relative "client_search/index_cache"
|
|
|
22
23
|
require_relative "client_search/ollama_embedding_adapter"
|
|
23
24
|
require_relative "client_search/generator"
|
|
24
25
|
require_relative "client_search/related_tag"
|
|
26
|
+
require_relative "client_search/dropdown_tag"
|
|
25
27
|
require_relative "client_search/search_tag"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-client-search
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Svend Gundestrup
|
|
@@ -46,6 +46,7 @@ files:
|
|
|
46
46
|
- assets/adapters/minisearch.js
|
|
47
47
|
- assets/adapters/semantic.js
|
|
48
48
|
- assets/client-search-base.js
|
|
49
|
+
- assets/client-search-dropdown.js
|
|
49
50
|
- assets/client-search-related.js
|
|
50
51
|
- assets/includes/related-articles.html
|
|
51
52
|
- assets/layouts/post-with-related.html
|
|
@@ -59,6 +60,8 @@ files:
|
|
|
59
60
|
- lib/jekyll/client_search/configuration.rb
|
|
60
61
|
- lib/jekyll/client_search/configuration_accessors.rb
|
|
61
62
|
- lib/jekyll/client_search/document_builder.rb
|
|
63
|
+
- lib/jekyll/client_search/dropdown_configuration.rb
|
|
64
|
+
- lib/jekyll/client_search/dropdown_tag.rb
|
|
62
65
|
- lib/jekyll/client_search/embedder_config_page.rb
|
|
63
66
|
- lib/jekyll/client_search/embedding_configuration.rb
|
|
64
67
|
- lib/jekyll/client_search/generator.rb
|