docyard 0.0.1 → 0.1.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/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- data/.github/pull_request_template.md +14 -0
- data/.github/workflows/ci.yml +49 -0
- data/.rubocop.yml +35 -0
- data/CHANGELOG.md +30 -0
- data/CONTRIBUTING.md +55 -0
- data/README.md +166 -3
- data/lib/docyard/asset_handler.rb +64 -0
- data/lib/docyard/cli.rb +34 -0
- data/lib/docyard/file_watcher.rb +42 -0
- data/lib/docyard/initializer.rb +69 -0
- data/lib/docyard/markdown.rb +61 -0
- data/lib/docyard/rack_application.rb +63 -0
- data/lib/docyard/renderer.rb +59 -0
- data/lib/docyard/router.rb +26 -0
- data/lib/docyard/server.rb +91 -0
- data/lib/docyard/templates/assets/css/main.css +4 -0
- data/lib/docyard/templates/assets/css/syntax.css +116 -0
- data/lib/docyard/templates/assets/js/reload.js +98 -0
- data/lib/docyard/templates/assets/js/theme.js +1 -0
- data/lib/docyard/templates/errors/404.html.erb +16 -0
- data/lib/docyard/templates/errors/500.html.erb +25 -0
- data/lib/docyard/templates/layouts/default.html.erb +29 -0
- data/lib/docyard/templates/markdown/getting-started.md.erb +40 -0
- data/lib/docyard/templates/markdown/index.md.erb +22 -0
- data/lib/docyard/version.rb +1 -1
- data/lib/docyard.rb +7 -1
- metadata +140 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "rack"
|
|
5
|
+
|
|
6
|
+
module Docyard
|
|
7
|
+
class RackApplication
|
|
8
|
+
def initialize(docs_path:, file_watcher:)
|
|
9
|
+
@docs_path = docs_path
|
|
10
|
+
@file_watcher = file_watcher
|
|
11
|
+
@router = Router.new(docs_path: docs_path)
|
|
12
|
+
@renderer = Renderer.new
|
|
13
|
+
@asset_handler = AssetHandler.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(env)
|
|
17
|
+
handle_request(env)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :docs_path, :file_watcher, :router, :renderer, :asset_handler
|
|
23
|
+
|
|
24
|
+
def handle_request(env)
|
|
25
|
+
path = env["PATH_INFO"]
|
|
26
|
+
|
|
27
|
+
return handle_reload_check(env) if path == "/_docyard/reload"
|
|
28
|
+
return asset_handler.serve(path) if path.start_with?("/assets/")
|
|
29
|
+
|
|
30
|
+
handle_documentation_request(path)
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
handle_error(e)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def handle_documentation_request(path)
|
|
36
|
+
file_path = router.resolve(path)
|
|
37
|
+
status = file_path ? 200 : 404
|
|
38
|
+
html = file_path ? renderer.render_file(file_path) : renderer.render_not_found
|
|
39
|
+
|
|
40
|
+
[status, { "Content-Type" => "text/html; charset=utf-8" }, [html]]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def handle_reload_check(env)
|
|
44
|
+
query = Rack::Utils.parse_query(env["QUERY_STRING"])
|
|
45
|
+
since = query["since"] ? Time.at(query["since"].to_f) : Time.now
|
|
46
|
+
reload_needed = file_watcher.changed_since?(since)
|
|
47
|
+
|
|
48
|
+
build_reload_response(reload_needed)
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
puts "[Docyard] Reload check error: #{e.message}"
|
|
51
|
+
build_reload_response(false)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_reload_response(reload_needed)
|
|
55
|
+
response_body = { reload: reload_needed, timestamp: Time.now.to_f }.to_json
|
|
56
|
+
[200, { "Content-Type" => "application/json" }, [response_body]]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def handle_error(error)
|
|
60
|
+
[500, { "Content-Type" => "text/html; charset=utf-8" }, [renderer.render_server_error(error)]]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Docyard
|
|
6
|
+
class Renderer
|
|
7
|
+
LAYOUTS_PATH = File.join(__dir__, "templates", "layouts")
|
|
8
|
+
ERRORS_PATH = File.join(__dir__, "templates", "errors")
|
|
9
|
+
|
|
10
|
+
attr_reader :layout_path
|
|
11
|
+
|
|
12
|
+
def initialize(layout: "default")
|
|
13
|
+
@layout_path = File.join(LAYOUTS_PATH, "#{layout}.html.erb")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render_file(file_path)
|
|
17
|
+
markdown_content = File.read(file_path)
|
|
18
|
+
markdown = Markdown.new(markdown_content)
|
|
19
|
+
|
|
20
|
+
html_content = strip_md_from_links(markdown.html)
|
|
21
|
+
|
|
22
|
+
render(
|
|
23
|
+
content: html_content,
|
|
24
|
+
page_title: markdown.title || "Documentation"
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render(content:, page_title: "Documentation")
|
|
29
|
+
template = File.read(layout_path)
|
|
30
|
+
|
|
31
|
+
@content = content
|
|
32
|
+
@page_title = page_title
|
|
33
|
+
|
|
34
|
+
ERB.new(template).result(binding)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def render_not_found
|
|
38
|
+
render_error_template(404)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def render_server_error(error)
|
|
42
|
+
@error_message = error.message
|
|
43
|
+
@backtrace = error.backtrace.join("\n")
|
|
44
|
+
render_error_template(500)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def render_error_template(status)
|
|
48
|
+
error_template_path = File.join(ERRORS_PATH, "#{status}.html.erb")
|
|
49
|
+
template = File.read(error_template_path)
|
|
50
|
+
ERB.new(template).result(binding)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def strip_md_from_links(html)
|
|
56
|
+
html.gsub(/href="([^"]+)\.md"/, 'href="\1"')
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docyard
|
|
4
|
+
class Router
|
|
5
|
+
attr_reader :docs_path
|
|
6
|
+
|
|
7
|
+
def initialize(docs_path:)
|
|
8
|
+
@docs_path = docs_path
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def resolve(request_path)
|
|
12
|
+
clean_path = request_path.delete_prefix("/")
|
|
13
|
+
clean_path = "index" if clean_path.empty?
|
|
14
|
+
|
|
15
|
+
clean_path = clean_path.delete_suffix(".md")
|
|
16
|
+
|
|
17
|
+
file_path = File.join(docs_path, "#{clean_path}.md")
|
|
18
|
+
return file_path if File.file?(file_path)
|
|
19
|
+
|
|
20
|
+
index_path = File.join(docs_path, clean_path, "index.md")
|
|
21
|
+
return index_path if File.file?(index_path)
|
|
22
|
+
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webrick"
|
|
4
|
+
require "stringio"
|
|
5
|
+
require_relative "file_watcher"
|
|
6
|
+
require_relative "rack_application"
|
|
7
|
+
|
|
8
|
+
module Docyard
|
|
9
|
+
class Server
|
|
10
|
+
DEFAULT_PORT = 4200
|
|
11
|
+
DEFAULT_HOST = "localhost"
|
|
12
|
+
|
|
13
|
+
attr_reader :port, :host, :docs_path
|
|
14
|
+
|
|
15
|
+
def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, docs_path: "docs")
|
|
16
|
+
@port = port
|
|
17
|
+
@host = host
|
|
18
|
+
@docs_path = docs_path
|
|
19
|
+
@file_watcher = FileWatcher.new(File.expand_path(docs_path))
|
|
20
|
+
@app = RackApplication.new(
|
|
21
|
+
docs_path: File.expand_path(docs_path),
|
|
22
|
+
file_watcher: @file_watcher
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start
|
|
27
|
+
validate_docs_directory!
|
|
28
|
+
print_server_info
|
|
29
|
+
@file_watcher.start
|
|
30
|
+
|
|
31
|
+
http_server.mount_proc("/") { |req, res| handle_request(req, res) }
|
|
32
|
+
trap("INT") { shutdown_server }
|
|
33
|
+
|
|
34
|
+
http_server.start
|
|
35
|
+
@file_watcher.stop
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def validate_docs_directory!
|
|
41
|
+
return if File.directory?(docs_path)
|
|
42
|
+
|
|
43
|
+
abort "Error: #{docs_path}/ directory not found.\n" \
|
|
44
|
+
"Run `docyard init` first to create the docs structure."
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def print_server_info
|
|
48
|
+
puts "Starting Docyard server..."
|
|
49
|
+
puts "=> Serving docs from: #{docs_path}/"
|
|
50
|
+
puts "=> Running at: http://#{host}:#{port}"
|
|
51
|
+
puts "=> Press Ctrl+C to stop\n"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def shutdown_server
|
|
55
|
+
puts "\nShutting down server..."
|
|
56
|
+
http_server.shutdown
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def http_server
|
|
60
|
+
@http_server ||= WEBrick::HTTPServer.new(
|
|
61
|
+
Port: port,
|
|
62
|
+
BindAddress: host,
|
|
63
|
+
AccessLog: [],
|
|
64
|
+
Logger: WEBrick::Log.new(File::NULL)
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def handle_request(req, res)
|
|
69
|
+
env = build_rack_env(req)
|
|
70
|
+
status, headers, body = @app.call(env)
|
|
71
|
+
|
|
72
|
+
res.status = status
|
|
73
|
+
headers.each { |key, value| res[key] = value }
|
|
74
|
+
body.each { |chunk| res.body << chunk }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_rack_env(req)
|
|
78
|
+
{
|
|
79
|
+
"REQUEST_METHOD" => req.request_method,
|
|
80
|
+
"PATH_INFO" => req.path,
|
|
81
|
+
"QUERY_STRING" => req.query_string || "",
|
|
82
|
+
"SERVER_NAME" => req.host,
|
|
83
|
+
"SERVER_PORT" => req.port.to_s,
|
|
84
|
+
"rack.version" => Rack::VERSION,
|
|
85
|
+
"rack.url_scheme" => "http",
|
|
86
|
+
"rack.input" => StringIO.new,
|
|
87
|
+
"rack.errors" => $stderr
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
.highlight table td { padding: 5px; }
|
|
2
|
+
.highlight table pre { margin: 0; }
|
|
3
|
+
.highlight, .highlight .w {
|
|
4
|
+
color: #24292f;
|
|
5
|
+
background-color: #f6f8fa;
|
|
6
|
+
}
|
|
7
|
+
.highlight .k, .highlight .kd, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kt, .highlight .kv {
|
|
8
|
+
color: #cf222e;
|
|
9
|
+
}
|
|
10
|
+
.highlight .gr {
|
|
11
|
+
color: #f6f8fa;
|
|
12
|
+
}
|
|
13
|
+
.highlight .gd {
|
|
14
|
+
color: #82071e;
|
|
15
|
+
background-color: #ffebe9;
|
|
16
|
+
}
|
|
17
|
+
.highlight .nb {
|
|
18
|
+
color: #953800;
|
|
19
|
+
}
|
|
20
|
+
.highlight .nc {
|
|
21
|
+
color: #953800;
|
|
22
|
+
}
|
|
23
|
+
.highlight .no {
|
|
24
|
+
color: #953800;
|
|
25
|
+
}
|
|
26
|
+
.highlight .nn {
|
|
27
|
+
color: #953800;
|
|
28
|
+
}
|
|
29
|
+
.highlight .sr {
|
|
30
|
+
color: #116329;
|
|
31
|
+
}
|
|
32
|
+
.highlight .na {
|
|
33
|
+
color: #116329;
|
|
34
|
+
}
|
|
35
|
+
.highlight .nt {
|
|
36
|
+
color: #116329;
|
|
37
|
+
}
|
|
38
|
+
.highlight .gi {
|
|
39
|
+
color: #116329;
|
|
40
|
+
background-color: #dafbe1;
|
|
41
|
+
}
|
|
42
|
+
.highlight .ges {
|
|
43
|
+
font-weight: bold;
|
|
44
|
+
font-style: italic;
|
|
45
|
+
}
|
|
46
|
+
.highlight .kc {
|
|
47
|
+
color: #0550ae;
|
|
48
|
+
}
|
|
49
|
+
.highlight .l, .highlight .ld, .highlight .m, .highlight .mb, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo, .highlight .mx {
|
|
50
|
+
color: #0550ae;
|
|
51
|
+
}
|
|
52
|
+
.highlight .sb {
|
|
53
|
+
color: #0550ae;
|
|
54
|
+
}
|
|
55
|
+
.highlight .bp {
|
|
56
|
+
color: #0550ae;
|
|
57
|
+
}
|
|
58
|
+
.highlight .ne {
|
|
59
|
+
color: #0550ae;
|
|
60
|
+
}
|
|
61
|
+
.highlight .nl {
|
|
62
|
+
color: #0550ae;
|
|
63
|
+
}
|
|
64
|
+
.highlight .py {
|
|
65
|
+
color: #0550ae;
|
|
66
|
+
}
|
|
67
|
+
.highlight .nv, .highlight .vc, .highlight .vg, .highlight .vi, .highlight .vm {
|
|
68
|
+
color: #0550ae;
|
|
69
|
+
}
|
|
70
|
+
.highlight .o, .highlight .ow {
|
|
71
|
+
color: #0550ae;
|
|
72
|
+
}
|
|
73
|
+
.highlight .gh {
|
|
74
|
+
color: #0550ae;
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
}
|
|
77
|
+
.highlight .gu {
|
|
78
|
+
color: #0550ae;
|
|
79
|
+
font-weight: bold;
|
|
80
|
+
}
|
|
81
|
+
.highlight .s, .highlight .sa, .highlight .sc, .highlight .dl, .highlight .sd, .highlight .s2, .highlight .se, .highlight .sh, .highlight .sx, .highlight .s1, .highlight .ss {
|
|
82
|
+
color: #0a3069;
|
|
83
|
+
}
|
|
84
|
+
.highlight .nd {
|
|
85
|
+
color: #8250df;
|
|
86
|
+
}
|
|
87
|
+
.highlight .nf, .highlight .fm {
|
|
88
|
+
color: #8250df;
|
|
89
|
+
}
|
|
90
|
+
.highlight .err {
|
|
91
|
+
color: #f6f8fa;
|
|
92
|
+
background-color: #82071e;
|
|
93
|
+
}
|
|
94
|
+
.highlight .c, .highlight .ch, .highlight .cd, .highlight .cm, .highlight .cp, .highlight .cpf, .highlight .c1, .highlight .cs {
|
|
95
|
+
color: #6e7781;
|
|
96
|
+
}
|
|
97
|
+
.highlight .gl {
|
|
98
|
+
color: #6e7781;
|
|
99
|
+
}
|
|
100
|
+
.highlight .gt {
|
|
101
|
+
color: #6e7781;
|
|
102
|
+
}
|
|
103
|
+
.highlight .ni {
|
|
104
|
+
color: #24292f;
|
|
105
|
+
}
|
|
106
|
+
.highlight .si {
|
|
107
|
+
color: #24292f;
|
|
108
|
+
}
|
|
109
|
+
.highlight .ge {
|
|
110
|
+
color: #24292f;
|
|
111
|
+
font-style: italic;
|
|
112
|
+
}
|
|
113
|
+
.highlight .gs {
|
|
114
|
+
color: #24292f;
|
|
115
|
+
font-weight: bold;
|
|
116
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
let lastCheck = Date.now() / 1000;
|
|
3
|
+
let basePollInterval = 500;
|
|
4
|
+
let currentPollInterval = basePollInterval;
|
|
5
|
+
let isReloading = false;
|
|
6
|
+
let consecutiveFailures = 0;
|
|
7
|
+
let serverWasDown = false;
|
|
8
|
+
let timeoutId = null;
|
|
9
|
+
|
|
10
|
+
async function fetchWithTimeout(resource, options = {}) {
|
|
11
|
+
const { timeout = 5000 } = options;
|
|
12
|
+
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch(resource, {
|
|
18
|
+
...options,
|
|
19
|
+
signal: controller.signal
|
|
20
|
+
});
|
|
21
|
+
clearTimeout(id);
|
|
22
|
+
return response;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
clearTimeout(id);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function checkForChanges() {
|
|
30
|
+
if (isReloading) return;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetchWithTimeout(`/_docyard/reload?since=${lastCheck}`, { timeout: 3000 });
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
lastCheck = data.timestamp;
|
|
36
|
+
|
|
37
|
+
if (consecutiveFailures > 0) {
|
|
38
|
+
consecutiveFailures = 0;
|
|
39
|
+
currentPollInterval = basePollInterval;
|
|
40
|
+
if (serverWasDown) {
|
|
41
|
+
console.log('[Docyard] Server reconnected');
|
|
42
|
+
serverWasDown = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (data.reload && !isReloading) {
|
|
47
|
+
isReloading = true;
|
|
48
|
+
console.log("[Docyard] Changes detected, hot reloading...");
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const resp = await fetchWithTimeout(window.location.href, { timeout: 5000 });
|
|
52
|
+
const html = await resp.text();
|
|
53
|
+
const parser = new DOMParser();
|
|
54
|
+
const newDoc = parser.parseFromString(html, 'text/html');
|
|
55
|
+
|
|
56
|
+
const oldMain = document.querySelector('main');
|
|
57
|
+
const newMain = newDoc.querySelector('main');
|
|
58
|
+
|
|
59
|
+
if (oldMain && newMain) {
|
|
60
|
+
oldMain.innerHTML = newMain.innerHTML;
|
|
61
|
+
console.log('[Docyard] Content updated via hot reload');
|
|
62
|
+
} else {
|
|
63
|
+
console.log('[Docyard] Layout changed, full reload required');
|
|
64
|
+
window.location.reload();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
isReloading = false;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('[Docyard] Hot reload failed:', error);
|
|
71
|
+
console.log('[Docyard] Falling back to full reload');
|
|
72
|
+
window.location.reload();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
consecutiveFailures++;
|
|
77
|
+
|
|
78
|
+
if (consecutiveFailures === 1) {
|
|
79
|
+
console.log('[Docyard] Server disconnected - live reload paused');
|
|
80
|
+
serverWasDown = true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (consecutiveFailures >= 3) {
|
|
84
|
+
console.log('[Docyard] Stopped polling. Refresh page when server restarts.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
currentPollInterval = Math.min(basePollInterval * Math.pow(2, consecutiveFailures - 1), 5000);
|
|
89
|
+
|
|
90
|
+
isReloading = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
timeoutId = setTimeout(checkForChanges, currentPollInterval);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
checkForChanges();
|
|
97
|
+
console.log("[Docyard] Hot reload initialized");
|
|
98
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log('Docyard loaded');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>404 - Page Not Found</title>
|
|
7
|
+
<link rel="stylesheet" href="/assets/css/main.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>404 - Page Not Found</h1>
|
|
12
|
+
<p>The page you're looking for doesn't exist.</p>
|
|
13
|
+
<p><a href="/">Go back home</a></p>
|
|
14
|
+
</main>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>500 - Server Error</title>
|
|
7
|
+
<link rel="stylesheet" href="/assets/css/main.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>500 - Internal Server Error</h1>
|
|
12
|
+
<p>Something went wrong.</p>
|
|
13
|
+
|
|
14
|
+
<% if @error_message %>
|
|
15
|
+
<h3>Error Details:</h3>
|
|
16
|
+
<pre><%= @error_message %></pre>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<% if @backtrace %>
|
|
20
|
+
<h3>Backtrace:</h3>
|
|
21
|
+
<pre><%= @backtrace %></pre>
|
|
22
|
+
<% end %>
|
|
23
|
+
</main>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= @page_title || 'Documentation' %></title>
|
|
7
|
+
<link rel="stylesheet" href="/assets/css/main.css">
|
|
8
|
+
<link rel="stylesheet" href="/assets/css/syntax.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header>
|
|
12
|
+
<h1>Documentation</h1>
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<nav>
|
|
16
|
+
<!-- Sidebar will go here -->
|
|
17
|
+
</nav>
|
|
18
|
+
|
|
19
|
+
<main>
|
|
20
|
+
<%= @content %>
|
|
21
|
+
</main>
|
|
22
|
+
|
|
23
|
+
<footer>
|
|
24
|
+
<p>Built with Docyard</p>
|
|
25
|
+
</footer>
|
|
26
|
+
|
|
27
|
+
<script src="/assets/js/reload.js"></script>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting Started
|
|
3
|
+
description: Learn how to use Docyard
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Getting Started
|
|
7
|
+
|
|
8
|
+
Welcome! This guide will help you get started with Docyard.
|
|
9
|
+
|
|
10
|
+
## Writing Documentation
|
|
11
|
+
|
|
12
|
+
Just write markdown files in the `docs/` folder.
|
|
13
|
+
|
|
14
|
+
### Frontmatter
|
|
15
|
+
|
|
16
|
+
Each markdown file can have YAML frontmatter:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
---
|
|
20
|
+
title: Page Title
|
|
21
|
+
description: Page description
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
Markdown Syntax
|
|
25
|
+
|
|
26
|
+
Docyard supports all standard markdown:
|
|
27
|
+
|
|
28
|
+
- Bold text
|
|
29
|
+
- Italic text
|
|
30
|
+
- Code snippets
|
|
31
|
+
- Links and images
|
|
32
|
+
- Code blocks with syntax highlighting
|
|
33
|
+
|
|
34
|
+
Next Steps
|
|
35
|
+
|
|
36
|
+
- Add more markdown files to docs/
|
|
37
|
+
- Organize files into folders
|
|
38
|
+
- Customize your site with docyard.yml
|
|
39
|
+
|
|
40
|
+
Happy documenting! 📚
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Home
|
|
3
|
+
description: Welcome to your documentation site
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Welcome to Docyard!
|
|
7
|
+
|
|
8
|
+
This is your documentation homepage. Edit this file to customize it.
|
|
9
|
+
|
|
10
|
+
## What is Docyard?
|
|
11
|
+
|
|
12
|
+
Docyard is a beautiful, zero-config documentation generator built with Ruby.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
1. Edit the markdown files in `docs/`
|
|
17
|
+
2. Run `docyard serve` to preview your site
|
|
18
|
+
3. Run `docyard build` to generate static HTML
|
|
19
|
+
|
|
20
|
+
## Learn More
|
|
21
|
+
|
|
22
|
+
Check out the [Getting Started](getting-started.md) guide.
|
data/lib/docyard/version.rb
CHANGED
data/lib/docyard.rb
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "docyard/version"
|
|
4
|
+
require_relative "docyard/cli"
|
|
5
|
+
require_relative "docyard/initializer"
|
|
6
|
+
require_relative "docyard/markdown"
|
|
7
|
+
require_relative "docyard/router"
|
|
8
|
+
require_relative "docyard/renderer"
|
|
9
|
+
require_relative "docyard/asset_handler"
|
|
10
|
+
require_relative "docyard/server"
|
|
4
11
|
|
|
5
12
|
module Docyard
|
|
6
13
|
class Error < StandardError; end
|
|
7
|
-
# Your code goes here...
|
|
8
14
|
end
|