docyard 0.5.0 → 0.7.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/.rubocop.yml +1 -1
- data/CHANGELOG.md +34 -1
- data/lib/docyard/build/static_generator.rb +3 -44
- data/lib/docyard/builder.rb +14 -4
- data/lib/docyard/cli.rb +6 -3
- data/lib/docyard/components/aliases.rb +29 -0
- data/lib/docyard/components/base_processor.rb +6 -0
- data/lib/docyard/components/processors/callout_processor.rb +124 -0
- data/lib/docyard/components/processors/code_block_diff_preprocessor.rb +106 -0
- data/lib/docyard/components/processors/code_block_focus_preprocessor.rb +79 -0
- data/lib/docyard/components/processors/code_block_options_preprocessor.rb +78 -0
- data/lib/docyard/components/processors/code_block_processor.rb +175 -0
- data/lib/docyard/components/processors/code_snippet_import_preprocessor.rb +127 -0
- data/lib/docyard/components/processors/heading_anchor_processor.rb +39 -0
- data/lib/docyard/components/processors/icon_processor.rb +53 -0
- data/lib/docyard/components/processors/table_of_contents_processor.rb +68 -0
- data/lib/docyard/components/processors/table_wrapper_processor.rb +22 -0
- data/lib/docyard/components/processors/tabs_processor.rb +48 -0
- data/lib/docyard/components/registry.rb +4 -4
- data/lib/docyard/components/support/code_block/feature_extractor.rb +117 -0
- data/lib/docyard/components/support/code_block/icon_detector.rb +44 -0
- data/lib/docyard/components/support/code_block/line_parser.rb +84 -0
- data/lib/docyard/components/support/code_block/line_wrapper.rb +50 -0
- data/lib/docyard/components/support/code_block/patterns.rb +55 -0
- data/lib/docyard/components/support/code_detector.rb +61 -0
- data/lib/docyard/components/support/tabs/icon_detector.rb +62 -0
- data/lib/docyard/components/support/tabs/parser.rb +195 -0
- data/lib/docyard/components/support/tabs/range_finder.rb +46 -0
- data/lib/docyard/config/branding_resolver.rb +74 -0
- data/lib/docyard/{constants.rb → config/constants.rb} +1 -0
- data/lib/docyard/config/validator.rb +8 -0
- data/lib/docyard/config.rb +17 -1
- data/lib/docyard/{prev_next_builder.rb → navigation/prev_next_builder.rb} +2 -2
- data/lib/docyard/{sidebar → navigation/sidebar}/renderer.rb +3 -14
- data/lib/docyard/{sidebar → navigation/sidebar}/tree_builder.rb +9 -2
- data/lib/docyard/{sidebar_builder.rb → navigation/sidebar_builder.rb} +3 -15
- data/lib/docyard/{icons → rendering/icons}/file_types.rb +0 -13
- data/lib/docyard/{icons → rendering/icons}/phosphor.rb +4 -1
- data/lib/docyard/{markdown.rb → rendering/markdown.rb} +23 -14
- data/lib/docyard/{renderer.rb → rendering/renderer.rb} +24 -20
- data/lib/docyard/search/build_indexer.rb +74 -0
- data/lib/docyard/search/dev_indexer.rb +110 -0
- data/lib/docyard/search/pagefind_support.rb +31 -0
- data/lib/docyard/{asset_handler.rb → server/asset_handler.rb} +1 -1
- data/lib/docyard/{server.rb → server/dev_server.rb} +32 -9
- data/lib/docyard/{preview_server.rb → server/preview_server.rb} +1 -1
- data/lib/docyard/{rack_application.rb → server/rack_application.rb} +53 -50
- data/lib/docyard/server/resolution_result.rb +29 -0
- data/lib/docyard/{router.rb → server/router.rb} +4 -4
- data/lib/docyard/templates/assets/css/code.css +12 -4
- data/lib/docyard/templates/assets/css/components/code-block.css +427 -24
- data/lib/docyard/templates/assets/css/components/navigation.css +12 -9
- data/lib/docyard/templates/assets/css/components/search.css +549 -0
- data/lib/docyard/templates/assets/css/components/tabs.css +50 -44
- data/lib/docyard/templates/assets/css/layout.css +15 -1
- data/lib/docyard/templates/assets/css/variables.css +44 -0
- data/lib/docyard/templates/assets/js/components/search.js +685 -0
- data/lib/docyard/templates/layouts/default.html.erb +14 -2
- data/lib/docyard/templates/partials/_code_block.html.erb +50 -2
- data/lib/docyard/templates/partials/_heading_anchor.html.erb +1 -1
- data/lib/docyard/templates/partials/_prev_next.html.erb +1 -1
- data/lib/docyard/templates/partials/_search_modal.html.erb +45 -0
- data/lib/docyard/templates/partials/_search_trigger.html.erb +22 -0
- data/lib/docyard/utils/html_helpers.rb +14 -0
- data/lib/docyard/utils/path_resolver.rb +2 -1
- data/lib/docyard/utils/url_helpers.rb +20 -0
- data/lib/docyard/version.rb +1 -1
- data/lib/docyard.rb +22 -15
- metadata +57 -36
- data/lib/docyard/components/callout_processor.rb +0 -121
- data/lib/docyard/components/code_block_processor.rb +0 -55
- data/lib/docyard/components/code_detector.rb +0 -59
- data/lib/docyard/components/heading_anchor_processor.rb +0 -34
- data/lib/docyard/components/icon_detector.rb +0 -57
- data/lib/docyard/components/icon_processor.rb +0 -51
- data/lib/docyard/components/table_of_contents_processor.rb +0 -64
- data/lib/docyard/components/table_wrapper_processor.rb +0 -18
- data/lib/docyard/components/tabs_parser.rb +0 -60
- data/lib/docyard/components/tabs_processor.rb +0 -44
- data/lib/docyard/routing/resolution_result.rb +0 -31
- /data/lib/docyard/{sidebar → navigation/sidebar}/config_parser.rb +0 -0
- /data/lib/docyard/{sidebar → navigation/sidebar}/file_system_scanner.rb +0 -0
- /data/lib/docyard/{sidebar → navigation/sidebar}/item.rb +0 -0
- /data/lib/docyard/{sidebar → navigation/sidebar}/title_extractor.rb +0 -0
- /data/lib/docyard/{icons → rendering/icons}/LICENSE.phosphor +0 -0
- /data/lib/docyard/{icons.rb → rendering/icons.rb} +0 -0
- /data/lib/docyard/{language_mapping.rb → rendering/language_mapping.rb} +0 -0
- /data/lib/docyard/{file_watcher.rb → server/file_watcher.rb} +0 -0
- /data/lib/docyard/{errors.rb → utils/errors.rb} +0 -0
- /data/lib/docyard/{logging.rb → utils/logging.rb} +0 -0
|
@@ -4,30 +4,30 @@ require "webrick"
|
|
|
4
4
|
require "stringio"
|
|
5
5
|
require_relative "file_watcher"
|
|
6
6
|
require_relative "rack_application"
|
|
7
|
-
require_relative "config"
|
|
7
|
+
require_relative "../config"
|
|
8
8
|
|
|
9
9
|
module Docyard
|
|
10
10
|
class Server
|
|
11
11
|
DEFAULT_PORT = 4200
|
|
12
12
|
DEFAULT_HOST = "localhost"
|
|
13
13
|
|
|
14
|
-
attr_reader :port, :host, :docs_path, :config
|
|
14
|
+
attr_reader :port, :host, :docs_path, :config, :search_enabled
|
|
15
15
|
|
|
16
|
-
def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, docs_path: "docs")
|
|
16
|
+
def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, docs_path: "docs", search: false)
|
|
17
17
|
@port = port
|
|
18
18
|
@host = host
|
|
19
19
|
@docs_path = docs_path
|
|
20
|
+
@search_enabled = search
|
|
20
21
|
@config = Config.load
|
|
21
22
|
@file_watcher = FileWatcher.new(File.expand_path(docs_path))
|
|
22
|
-
@
|
|
23
|
-
|
|
24
|
-
file_watcher: @file_watcher,
|
|
25
|
-
config: @config
|
|
26
|
-
)
|
|
23
|
+
@search_indexer = nil
|
|
24
|
+
@app = nil
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
def start
|
|
30
28
|
validate_docs_directory!
|
|
29
|
+
generate_search_index if @search_enabled
|
|
30
|
+
initialize_app
|
|
31
31
|
print_server_info
|
|
32
32
|
@file_watcher.start
|
|
33
33
|
|
|
@@ -35,11 +35,33 @@ module Docyard
|
|
|
35
35
|
trap("INT") { shutdown_server }
|
|
36
36
|
|
|
37
37
|
http_server.start
|
|
38
|
-
|
|
38
|
+
cleanup
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
|
+
def generate_search_index
|
|
44
|
+
@search_indexer = Search::DevIndexer.new(
|
|
45
|
+
docs_path: File.expand_path(docs_path),
|
|
46
|
+
config: @config
|
|
47
|
+
)
|
|
48
|
+
@search_indexer.generate
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize_app
|
|
52
|
+
@app = RackApplication.new(
|
|
53
|
+
docs_path: File.expand_path(docs_path),
|
|
54
|
+
file_watcher: @file_watcher,
|
|
55
|
+
config: @config,
|
|
56
|
+
pagefind_path: @search_indexer&.pagefind_path
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def cleanup
|
|
61
|
+
@file_watcher.stop
|
|
62
|
+
@search_indexer&.cleanup
|
|
63
|
+
end
|
|
64
|
+
|
|
43
65
|
def validate_docs_directory!
|
|
44
66
|
return if File.directory?(docs_path)
|
|
45
67
|
|
|
@@ -51,6 +73,7 @@ module Docyard
|
|
|
51
73
|
puts "Starting Docyard server..."
|
|
52
74
|
puts "=> Serving docs from: #{docs_path}/"
|
|
53
75
|
puts "=> Running at: http://#{host}:#{port}"
|
|
76
|
+
puts "=> Search: #{@search_enabled ? 'enabled' : 'disabled (use --search to enable)'}"
|
|
54
77
|
puts "=> Press Ctrl+C to stop\n"
|
|
55
78
|
end
|
|
56
79
|
|
|
@@ -2,18 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "rack"
|
|
5
|
-
require_relative "sidebar_builder"
|
|
6
|
-
require_relative "prev_next_builder"
|
|
7
|
-
require_relative "
|
|
5
|
+
require_relative "../navigation/sidebar_builder"
|
|
6
|
+
require_relative "../navigation/prev_next_builder"
|
|
7
|
+
require_relative "../config/branding_resolver"
|
|
8
|
+
require_relative "../config/constants"
|
|
8
9
|
|
|
9
10
|
module Docyard
|
|
10
11
|
class RackApplication
|
|
11
|
-
|
|
12
|
+
PAGEFIND_CONTENT_TYPES = {
|
|
13
|
+
".js" => "application/javascript; charset=utf-8",
|
|
14
|
+
".css" => "text/css; charset=utf-8",
|
|
15
|
+
".json" => "application/json; charset=utf-8"
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
def initialize(docs_path:, file_watcher:, config: nil, pagefind_path: nil)
|
|
12
19
|
@docs_path = docs_path
|
|
13
20
|
@file_watcher = file_watcher
|
|
14
21
|
@config = config
|
|
22
|
+
@pagefind_path = pagefind_path
|
|
15
23
|
@router = Router.new(docs_path: docs_path)
|
|
16
|
-
@renderer = Renderer.new(base_url: config&.build&.base_url || "/")
|
|
24
|
+
@renderer = Renderer.new(base_url: config&.build&.base_url || "/", config: config)
|
|
17
25
|
@asset_handler = AssetHandler.new
|
|
18
26
|
end
|
|
19
27
|
|
|
@@ -23,13 +31,14 @@ module Docyard
|
|
|
23
31
|
|
|
24
32
|
private
|
|
25
33
|
|
|
26
|
-
attr_reader :docs_path, :file_watcher, :config, :router, :renderer, :asset_handler
|
|
34
|
+
attr_reader :docs_path, :file_watcher, :config, :pagefind_path, :router, :renderer, :asset_handler
|
|
27
35
|
|
|
28
36
|
def handle_request(env)
|
|
29
37
|
path = env["PATH_INFO"]
|
|
30
38
|
|
|
31
39
|
return handle_reload_check(env) if path == Constants::RELOAD_ENDPOINT
|
|
32
40
|
return asset_handler.serve(path) if path.start_with?(Constants::ASSETS_PREFIX)
|
|
41
|
+
return serve_pagefind(path) if path.start_with?(Constants::PAGEFIND_PREFIX)
|
|
33
42
|
|
|
34
43
|
handle_documentation_request(path)
|
|
35
44
|
rescue StandardError => e
|
|
@@ -91,50 +100,7 @@ module Docyard
|
|
|
91
100
|
end
|
|
92
101
|
|
|
93
102
|
def branding_options
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
default_branding.merge(config_branding_options)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def default_branding
|
|
100
|
-
{
|
|
101
|
-
site_title: Constants::DEFAULT_SITE_TITLE,
|
|
102
|
-
site_description: "",
|
|
103
|
-
logo: Constants::DEFAULT_LOGO_PATH,
|
|
104
|
-
logo_dark: Constants::DEFAULT_LOGO_DARK_PATH,
|
|
105
|
-
favicon: nil,
|
|
106
|
-
display_logo: true,
|
|
107
|
-
display_title: true
|
|
108
|
-
}
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def config_branding_options
|
|
112
|
-
site = config.site
|
|
113
|
-
branding = config.branding
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
site_title: site.title || Constants::DEFAULT_SITE_TITLE,
|
|
117
|
-
site_description: site.description || "",
|
|
118
|
-
logo: resolve_logo(branding.logo, branding.logo_dark),
|
|
119
|
-
logo_dark: resolve_logo_dark(branding.logo, branding.logo_dark),
|
|
120
|
-
favicon: branding.favicon
|
|
121
|
-
}.merge(appearance_options(branding.appearance))
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def appearance_options(appearance)
|
|
125
|
-
appearance ||= {}
|
|
126
|
-
{
|
|
127
|
-
display_logo: appearance["logo"] != false,
|
|
128
|
-
display_title: appearance["title"] != false
|
|
129
|
-
}
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def resolve_logo(logo, logo_dark)
|
|
133
|
-
logo || logo_dark || Constants::DEFAULT_LOGO_PATH
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def resolve_logo_dark(logo, logo_dark)
|
|
137
|
-
logo_dark || logo || Constants::DEFAULT_LOGO_DARK_PATH
|
|
103
|
+
BrandingResolver.new(config).resolve
|
|
138
104
|
end
|
|
139
105
|
|
|
140
106
|
def handle_reload_check(env)
|
|
@@ -168,5 +134,42 @@ module Docyard
|
|
|
168
134
|
[Constants::STATUS_INTERNAL_ERROR, { "Content-Type" => Constants::CONTENT_TYPE_HTML },
|
|
169
135
|
[renderer.render_server_error(error)]]
|
|
170
136
|
end
|
|
137
|
+
|
|
138
|
+
def serve_pagefind(path)
|
|
139
|
+
relative_path = path.delete_prefix(Constants::PAGEFIND_PREFIX)
|
|
140
|
+
return pagefind_not_found if relative_path.include?("..")
|
|
141
|
+
|
|
142
|
+
file_path = resolve_pagefind_file(relative_path)
|
|
143
|
+
return pagefind_not_found unless file_path && File.file?(file_path)
|
|
144
|
+
|
|
145
|
+
content = File.binread(file_path)
|
|
146
|
+
content_type = pagefind_content_type(file_path)
|
|
147
|
+
|
|
148
|
+
headers = {
|
|
149
|
+
"Content-Type" => content_type,
|
|
150
|
+
"Cache-Control" => "no-cache, no-store, must-revalidate",
|
|
151
|
+
"Pragma" => "no-cache",
|
|
152
|
+
"Expires" => "0"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[Constants::STATUS_OK, headers, [content]]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def resolve_pagefind_file(relative_path)
|
|
159
|
+
return File.join(pagefind_path, relative_path) if pagefind_path && Dir.exist?(pagefind_path)
|
|
160
|
+
|
|
161
|
+
output_dir = config&.build&.output_dir || "dist"
|
|
162
|
+
File.join(output_dir, "pagefind", relative_path)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def pagefind_content_type(file_path)
|
|
166
|
+
extension = File.extname(file_path)
|
|
167
|
+
PAGEFIND_CONTENT_TYPES.fetch(extension, "application/octet-stream")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def pagefind_not_found
|
|
171
|
+
message = "Pagefind not found. Run 'docyard build' first."
|
|
172
|
+
[Constants::STATUS_NOT_FOUND, { "Content-Type" => "text/plain" }, [message]]
|
|
173
|
+
end
|
|
171
174
|
end
|
|
172
175
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docyard
|
|
4
|
+
class ResolutionResult
|
|
5
|
+
attr_reader :file_path, :status
|
|
6
|
+
|
|
7
|
+
def self.found(file_path)
|
|
8
|
+
new(file_path: file_path, status: :found)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.not_found
|
|
12
|
+
new(file_path: nil, status: :not_found)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(file_path:, status:)
|
|
16
|
+
@file_path = file_path
|
|
17
|
+
@status = status
|
|
18
|
+
freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def found?
|
|
22
|
+
status == :found
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def not_found?
|
|
26
|
+
status == :not_found
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -12,18 +12,18 @@ module Docyard
|
|
|
12
12
|
clean_path = sanitize_path(request_path)
|
|
13
13
|
|
|
14
14
|
file_path = File.join(docs_path, "#{clean_path}#{Constants::MARKDOWN_EXTENSION}")
|
|
15
|
-
return
|
|
15
|
+
return ResolutionResult.found(file_path) if File.file?(file_path)
|
|
16
16
|
|
|
17
17
|
index_path = File.join(docs_path, clean_path, "#{Constants::INDEX_FILE}#{Constants::MARKDOWN_EXTENSION}")
|
|
18
|
-
return
|
|
18
|
+
return ResolutionResult.found(index_path) if File.file?(index_path)
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
ResolutionResult.not_found
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
25
|
def sanitize_path(request_path)
|
|
26
|
-
clean = request_path.delete_prefix("/")
|
|
26
|
+
clean = request_path.delete_prefix("/").delete_suffix("/")
|
|
27
27
|
clean = Constants::INDEX_FILE if clean.empty?
|
|
28
28
|
clean.delete_suffix(Constants::MARKDOWN_EXTENSION)
|
|
29
29
|
end
|
|
@@ -42,12 +42,16 @@
|
|
|
42
42
|
margin: 0;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
.highlight
|
|
46
|
-
.highlight .w {
|
|
45
|
+
.highlight {
|
|
47
46
|
color: #24292f;
|
|
48
47
|
background-color: #f6f8fa;
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
.highlight .w {
|
|
51
|
+
color: #24292f;
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
/* Keywords */
|
|
52
56
|
.highlight .k,
|
|
53
57
|
.highlight .kd,
|
|
@@ -214,12 +218,16 @@
|
|
|
214
218
|
}
|
|
215
219
|
|
|
216
220
|
/* Dark Mode Syntax Highlighting - GitHub Dark */
|
|
217
|
-
.dark .highlight
|
|
218
|
-
.dark .highlight .w {
|
|
221
|
+
.dark .highlight {
|
|
219
222
|
color: #e6edf3;
|
|
220
223
|
background-color: #161b22;
|
|
221
224
|
}
|
|
222
225
|
|
|
226
|
+
.dark .highlight .w {
|
|
227
|
+
color: #e6edf3;
|
|
228
|
+
background-color: transparent;
|
|
229
|
+
}
|
|
230
|
+
|
|
223
231
|
/* Keywords */
|
|
224
232
|
.dark .highlight .k,
|
|
225
233
|
.dark .highlight .kd,
|