docyard 1.1.0 → 1.2.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 +17 -1
- data/README.md +56 -11
- data/lib/docyard/build/asset_bundler.rb +17 -1
- data/lib/docyard/build/social_cards/card_renderer.rb +132 -0
- data/lib/docyard/build/social_cards/doc_card.rb +97 -0
- data/lib/docyard/build/social_cards/homepage_card.rb +98 -0
- data/lib/docyard/build/social_cards_generator.rb +188 -0
- data/lib/docyard/build/step_runner.rb +2 -0
- data/lib/docyard/builder.rb +10 -0
- data/lib/docyard/cli.rb +27 -0
- data/lib/docyard/config/branding_resolver.rb +1 -1
- data/lib/docyard/config/schema/definition.rb +2 -1
- data/lib/docyard/config/schema/sections.rb +0 -1
- data/lib/docyard/config/schema/simple_sections.rb +7 -0
- data/lib/docyard/config.rb +3 -1
- data/lib/docyard/customizer.rb +196 -0
- data/lib/docyard/rendering/markdown.rb +4 -0
- data/lib/docyard/rendering/og_helpers.rb +19 -1
- data/lib/docyard/rendering/renderer.rb +10 -1
- data/lib/docyard/search/build_indexer.rb +8 -3
- data/lib/docyard/search/dev_indexer.rb +3 -2
- data/lib/docyard/search/pagefind_binary.rb +185 -0
- data/lib/docyard/search/pagefind_support.rb +9 -7
- data/lib/docyard/server/asset_handler.rb +28 -2
- data/lib/docyard/server/dev_server.rb +1 -0
- data/lib/docyard/server/file_watcher.rb +10 -5
- data/lib/docyard/server/rack_application.rb +1 -1
- data/lib/docyard/templates/assets/css/variables.css +0 -6
- data/lib/docyard/templates/assets/js/components/abbreviation.js +20 -11
- data/lib/docyard/templates/assets/js/components/code-block.js +8 -3
- data/lib/docyard/templates/assets/js/components/code-group.js +20 -3
- data/lib/docyard/templates/assets/js/components/file-tree.js +9 -3
- data/lib/docyard/templates/assets/js/components/heading-anchor.js +71 -72
- data/lib/docyard/templates/assets/js/components/lightbox.js +10 -3
- data/lib/docyard/templates/assets/js/components/tabs.js +8 -3
- data/lib/docyard/templates/assets/js/components/tooltip.js +32 -23
- data/lib/docyard/templates/assets/js/hot-reload.js +42 -2
- data/lib/docyard/templates/partials/_head.html.erb +4 -0
- data/lib/docyard/templates/partials/_scripts.html.erb +3 -0
- data/lib/docyard/version.rb +1 -1
- data/lib/docyard.rb +2 -0
- metadata +7 -1
|
@@ -8,8 +8,9 @@ module Docyard
|
|
|
8
8
|
TEMPLATES_ASSETS_PATH = File.join(__dir__, "../templates", "assets")
|
|
9
9
|
CACHE_MAX_AGE = 3600
|
|
10
10
|
DEFAULT_PUBLIC_DIR = "docs/public"
|
|
11
|
+
DEFAULT_DOCS_PATH = "docs"
|
|
11
12
|
|
|
12
|
-
attr_reader :public_dir
|
|
13
|
+
attr_reader :public_dir, :docs_path
|
|
13
14
|
|
|
14
15
|
CONTENT_TYPES = {
|
|
15
16
|
".css" => "text/css; charset=utf-8",
|
|
@@ -30,8 +31,9 @@ module Docyard
|
|
|
30
31
|
".webm" => "video/webm"
|
|
31
32
|
}.freeze
|
|
32
33
|
|
|
33
|
-
def initialize(public_dir: DEFAULT_PUBLIC_DIR)
|
|
34
|
+
def initialize(public_dir: DEFAULT_PUBLIC_DIR, docs_path: DEFAULT_DOCS_PATH)
|
|
34
35
|
@public_dir = public_dir
|
|
36
|
+
@docs_path = docs_path
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
def serve_docyard_assets(request_path)
|
|
@@ -39,6 +41,8 @@ module Docyard
|
|
|
39
41
|
|
|
40
42
|
return serve_components_css if asset_path == "css/components.css"
|
|
41
43
|
return serve_components_js if asset_path == "js/components.js"
|
|
44
|
+
return serve_custom_css if asset_path == "css/custom.css"
|
|
45
|
+
return serve_custom_js if asset_path == "js/custom.js"
|
|
42
46
|
|
|
43
47
|
file_path = safe_asset_path(asset_path, TEMPLATES_ASSETS_PATH)
|
|
44
48
|
return forbidden_response unless file_path
|
|
@@ -94,6 +98,28 @@ module Docyard
|
|
|
94
98
|
[200, headers, [content]]
|
|
95
99
|
end
|
|
96
100
|
|
|
101
|
+
def serve_custom_css
|
|
102
|
+
custom_path = File.join(docs_path, "_custom", "styles.css")
|
|
103
|
+
return not_found_response unless File.exist?(custom_path)
|
|
104
|
+
|
|
105
|
+
content = File.read(custom_path)
|
|
106
|
+
headers = build_cache_headers(content, File.mtime(custom_path))
|
|
107
|
+
headers["Content-Type"] = "text/css; charset=utf-8"
|
|
108
|
+
|
|
109
|
+
[200, headers, [content]]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def serve_custom_js
|
|
113
|
+
custom_path = File.join(docs_path, "_custom", "scripts.js")
|
|
114
|
+
return not_found_response unless File.exist?(custom_path)
|
|
115
|
+
|
|
116
|
+
content = File.read(custom_path)
|
|
117
|
+
headers = build_cache_headers(content, File.mtime(custom_path))
|
|
118
|
+
headers["Content-Type"] = "application/javascript; charset=utf-8"
|
|
119
|
+
|
|
120
|
+
[200, headers, [content]]
|
|
121
|
+
end
|
|
122
|
+
|
|
97
123
|
def concatenate_component_js
|
|
98
124
|
components_dir = File.join(TEMPLATES_ASSETS_PATH, "js", "components")
|
|
99
125
|
return "" unless Dir.exist?(components_dir)
|
|
@@ -111,6 +111,7 @@ module Docyard
|
|
|
111
111
|
message = case change_type
|
|
112
112
|
when :content then "Content changed, reloading..."
|
|
113
113
|
when :full then "Config changed, full reload..."
|
|
114
|
+
when :css then "CSS changed, injecting styles..."
|
|
114
115
|
when :asset then "Asset changed, reloading..."
|
|
115
116
|
else "File changed, reloading..."
|
|
116
117
|
end
|
|
@@ -7,7 +7,8 @@ module Docyard
|
|
|
7
7
|
DEBOUNCE_DELAY = 0.1
|
|
8
8
|
CONFIG_FILES = %w[docyard.yml _sidebar.yml].freeze
|
|
9
9
|
CONTENT_EXTENSIONS = %w[.md .markdown].freeze
|
|
10
|
-
|
|
10
|
+
CSS_EXTENSIONS = %w[.css].freeze
|
|
11
|
+
ASSET_EXTENSIONS = %w[.js .html .erb].freeze
|
|
11
12
|
|
|
12
13
|
def initialize(docs_path:, on_change:)
|
|
13
14
|
@docs_path = File.expand_path(docs_path)
|
|
@@ -15,7 +16,7 @@ module Docyard
|
|
|
15
16
|
@on_change = on_change
|
|
16
17
|
@docs_listener = nil
|
|
17
18
|
@config_listener = nil
|
|
18
|
-
@pending_changes = { content: false, config: false, asset: false }
|
|
19
|
+
@pending_changes = { content: false, config: false, css: false, asset: false }
|
|
19
20
|
@debounce_timer = nil
|
|
20
21
|
@mutex = Mutex.new
|
|
21
22
|
end
|
|
@@ -51,12 +52,15 @@ module Docyard
|
|
|
51
52
|
|
|
52
53
|
def categorize_change(path)
|
|
53
54
|
filename = File.basename(path)
|
|
55
|
+
extension = File.extname(path)
|
|
54
56
|
|
|
55
57
|
if CONFIG_FILES.include?(filename)
|
|
56
58
|
@pending_changes[:config] = true
|
|
57
|
-
elsif CONTENT_EXTENSIONS.include?(
|
|
59
|
+
elsif CONTENT_EXTENSIONS.include?(extension)
|
|
58
60
|
@pending_changes[:content] = true
|
|
59
|
-
elsif
|
|
61
|
+
elsif CSS_EXTENSIONS.include?(extension)
|
|
62
|
+
@pending_changes[:css] = true
|
|
63
|
+
elsif ASSET_EXTENSIONS.include?(extension)
|
|
60
64
|
@pending_changes[:asset] = true
|
|
61
65
|
end
|
|
62
66
|
end
|
|
@@ -73,7 +77,7 @@ module Docyard
|
|
|
73
77
|
changes = nil
|
|
74
78
|
@mutex.synchronize do
|
|
75
79
|
changes = @pending_changes.dup
|
|
76
|
-
@pending_changes = { content: false, config: false, asset: false }
|
|
80
|
+
@pending_changes = { content: false, config: false, css: false, asset: false }
|
|
77
81
|
end
|
|
78
82
|
|
|
79
83
|
change_type = determine_change_type(changes)
|
|
@@ -83,6 +87,7 @@ module Docyard
|
|
|
83
87
|
def determine_change_type(changes)
|
|
84
88
|
return :full if changes[:config]
|
|
85
89
|
return :content if changes[:content]
|
|
90
|
+
return :css if changes[:css]
|
|
86
91
|
return :asset if changes[:asset]
|
|
87
92
|
|
|
88
93
|
nil
|
|
@@ -28,7 +28,7 @@ module Docyard
|
|
|
28
28
|
@router = Router.new(docs_path: docs_path)
|
|
29
29
|
@renderer = Renderer.new(base_url: "/", config: config, dev_mode: @dev_mode,
|
|
30
30
|
sse_port: sse_port)
|
|
31
|
-
@asset_handler = AssetHandler.new(public_dir: config&.public_dir || "docs/public")
|
|
31
|
+
@asset_handler = AssetHandler.new(public_dir: config&.public_dir || "docs/public", docs_path: docs_path)
|
|
32
32
|
@pagefind_handler = PagefindHandler.new(pagefind_path: pagefind_path, config: config)
|
|
33
33
|
@page_diagnostics = PageDiagnostics.new(docs_path) if @dev_mode
|
|
34
34
|
end
|
|
@@ -35,12 +35,6 @@
|
|
|
35
35
|
--sidebar-border: oklch(0.92 0.004 286.32);
|
|
36
36
|
--sidebar-ring: oklch(0.705 0.015 286.067);
|
|
37
37
|
|
|
38
|
-
--chart-1: oklch(0.87 0.12 207);
|
|
39
|
-
--chart-2: oklch(0.80 0.13 212);
|
|
40
|
-
--chart-3: oklch(0.71 0.13 215);
|
|
41
|
-
--chart-4: oklch(0.61 0.11 222);
|
|
42
|
-
--chart-5: oklch(0.52 0.09 223);
|
|
43
|
-
|
|
44
38
|
--code-background: oklch(0.967 0.001 286.375);
|
|
45
39
|
--code-foreground: oklch(0.141 0.005 285.823);
|
|
46
40
|
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
if (abbreviations.length === 0) return;
|
|
1
|
+
var abbrPopover = null;
|
|
2
|
+
var abbrHideTimeout = null;
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
function initializeAbbreviations(root = document) {
|
|
5
|
+
const abbreviations = root.querySelectorAll('.docyard-abbr');
|
|
6
|
+
if (abbreviations.length === 0) return;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
if (!abbrPopover) {
|
|
9
|
+
abbrPopover = createPopover();
|
|
10
|
+
document.body.appendChild(abbrPopover);
|
|
11
|
+
}
|
|
9
12
|
|
|
10
13
|
abbreviations.forEach(abbr => {
|
|
14
|
+
if (abbr.hasAttribute('data-abbr-initialized')) return;
|
|
15
|
+
abbr.setAttribute('data-abbr-initialized', 'true');
|
|
16
|
+
|
|
11
17
|
abbr.addEventListener('mouseenter', () => {
|
|
12
|
-
clearTimeout(
|
|
13
|
-
showPopover(
|
|
18
|
+
clearTimeout(abbrHideTimeout);
|
|
19
|
+
showPopover(abbrPopover, abbr);
|
|
14
20
|
});
|
|
15
21
|
|
|
16
22
|
abbr.addEventListener('mouseleave', () => {
|
|
17
|
-
|
|
18
|
-
hidePopover(
|
|
23
|
+
abbrHideTimeout = setTimeout(() => {
|
|
24
|
+
hidePopover(abbrPopover);
|
|
19
25
|
}, 100);
|
|
20
26
|
});
|
|
21
27
|
});
|
|
@@ -79,7 +85,10 @@ function hidePopover(popover) {
|
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
if (document.readyState === 'loading') {
|
|
82
|
-
document.addEventListener('DOMContentLoaded', initializeAbbreviations);
|
|
88
|
+
document.addEventListener('DOMContentLoaded', function() { initializeAbbreviations(); });
|
|
83
89
|
} else {
|
|
84
90
|
initializeAbbreviations();
|
|
85
91
|
}
|
|
92
|
+
|
|
93
|
+
window.docyard = window.docyard || {};
|
|
94
|
+
window.docyard.initAbbreviations = initializeAbbreviations;
|
|
@@ -125,20 +125,25 @@ class CodeBlockManager {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function initializeCodeBlocks() {
|
|
129
|
-
const codeBlocks =
|
|
128
|
+
function initializeCodeBlocks(root = document) {
|
|
129
|
+
const codeBlocks = root.querySelectorAll('.docyard-code-block');
|
|
130
130
|
|
|
131
131
|
codeBlocks.forEach(container => {
|
|
132
|
+
if (container.hasAttribute('data-code-block-initialized')) return;
|
|
133
|
+
container.setAttribute('data-code-block-initialized', 'true');
|
|
132
134
|
new CodeBlockManager(container);
|
|
133
135
|
});
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
if (document.readyState === 'loading') {
|
|
137
|
-
document.addEventListener('DOMContentLoaded', initializeCodeBlocks);
|
|
139
|
+
document.addEventListener('DOMContentLoaded', function() { initializeCodeBlocks(); });
|
|
138
140
|
} else {
|
|
139
141
|
initializeCodeBlocks();
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
window.docyard = window.docyard || {};
|
|
145
|
+
window.docyard.initCodeBlocks = initializeCodeBlocks;
|
|
146
|
+
|
|
142
147
|
if (typeof module !== 'undefined' && module.exports) {
|
|
143
148
|
module.exports = { CodeBlockManager };
|
|
144
149
|
}
|
|
@@ -9,6 +9,8 @@ class CodeGroupManager {
|
|
|
9
9
|
if (containers.length === 0) return;
|
|
10
10
|
|
|
11
11
|
containers.forEach(container => {
|
|
12
|
+
if (container.hasAttribute('data-code-group-initialized')) return;
|
|
13
|
+
container.setAttribute('data-code-group-initialized', 'true');
|
|
12
14
|
this.groups.push(new CodeGroup(container, this));
|
|
13
15
|
});
|
|
14
16
|
|
|
@@ -275,12 +277,27 @@ class CodeGroup {
|
|
|
275
277
|
}
|
|
276
278
|
}
|
|
277
279
|
|
|
278
|
-
function initializeCodeGroups() {
|
|
279
|
-
|
|
280
|
+
function initializeCodeGroups(root = document) {
|
|
281
|
+
const containers = root.querySelectorAll('.docyard-code-group');
|
|
282
|
+
if (containers.length === 0) return;
|
|
283
|
+
|
|
284
|
+
if (!window.docyardCodeGroupManager) {
|
|
285
|
+
window.docyardCodeGroupManager = new CodeGroupManager();
|
|
286
|
+
} else {
|
|
287
|
+
containers.forEach(container => {
|
|
288
|
+
if (container.hasAttribute('data-code-group-initialized')) return;
|
|
289
|
+
container.setAttribute('data-code-group-initialized', 'true');
|
|
290
|
+
window.docyardCodeGroupManager.groups.push(new CodeGroup(container, window.docyardCodeGroupManager));
|
|
291
|
+
});
|
|
292
|
+
window.docyardCodeGroupManager.loadPreference();
|
|
293
|
+
}
|
|
280
294
|
}
|
|
281
295
|
|
|
282
296
|
if (document.readyState === 'loading') {
|
|
283
|
-
document.addEventListener('DOMContentLoaded', initializeCodeGroups);
|
|
297
|
+
document.addEventListener('DOMContentLoaded', function() { initializeCodeGroups(); });
|
|
284
298
|
} else {
|
|
285
299
|
initializeCodeGroups();
|
|
286
300
|
}
|
|
301
|
+
|
|
302
|
+
window.docyard = window.docyard || {};
|
|
303
|
+
window.docyard.initCodeGroups = initializeCodeGroups;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
function initializeFileTrees() {
|
|
2
|
-
const fileTrees =
|
|
1
|
+
function initializeFileTrees(root = document) {
|
|
2
|
+
const fileTrees = root.querySelectorAll('.docyard-filetree');
|
|
3
3
|
|
|
4
4
|
fileTrees.forEach(tree => {
|
|
5
|
+
if (tree.hasAttribute('data-filetree-initialized')) return;
|
|
6
|
+
tree.setAttribute('data-filetree-initialized', 'true');
|
|
7
|
+
|
|
5
8
|
const folders = tree.querySelectorAll('.docyard-filetree__item--folder');
|
|
6
9
|
|
|
7
10
|
folders.forEach(folder => {
|
|
@@ -33,7 +36,10 @@ function initializeFileTrees() {
|
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
if (document.readyState === 'loading') {
|
|
36
|
-
document.addEventListener('DOMContentLoaded', initializeFileTrees);
|
|
39
|
+
document.addEventListener('DOMContentLoaded', function() { initializeFileTrees(); });
|
|
37
40
|
} else {
|
|
38
41
|
initializeFileTrees();
|
|
39
42
|
}
|
|
43
|
+
|
|
44
|
+
window.docyard = window.docyard || {};
|
|
45
|
+
window.docyard.initFileTrees = initializeFileTrees;
|
|
@@ -1,92 +1,91 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
1
|
+
function initializeHeadingAnchors(root = document) {
|
|
2
|
+
const anchors = root.querySelectorAll('.heading-anchor');
|
|
3
|
+
if (anchors.length === 0) return;
|
|
4
|
+
|
|
5
|
+
anchors.forEach(anchor => {
|
|
6
|
+
if (anchor.hasAttribute('data-anchor-initialized')) return;
|
|
7
|
+
anchor.setAttribute('data-anchor-initialized', 'true');
|
|
8
|
+
anchor.addEventListener('click', (e) => handleHeadingAnchorClick(e, anchor));
|
|
9
|
+
});
|
|
10
|
+
}
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
e.preventDefault();
|
|
12
|
+
function handleHeadingAnchorClick(e, anchor) {
|
|
13
|
+
e.preventDefault();
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const headingId = anchor.dataset.headingId;
|
|
16
|
+
const url = `${window.location.origin}${window.location.pathname}#${headingId}`;
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
copyAnchorToClipboard(url, anchor);
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
history.pushState(null, null, `#${headingId}`);
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
22
|
+
const heading = document.getElementById(headingId);
|
|
23
|
+
if (heading) {
|
|
24
|
+
const offsetTop = heading.getBoundingClientRect().top + window.pageYOffset - getAnchorScrollOffset();
|
|
25
|
+
window.scrollTo({
|
|
26
|
+
top: offsetTop,
|
|
27
|
+
behavior: 'smooth'
|
|
28
|
+
});
|
|
32
29
|
}
|
|
30
|
+
}
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (window.innerWidth > 1024 && window.innerWidth <= 1280) {
|
|
41
|
-
return headerHeight + 48 + buffer;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (window.innerWidth <= 1024) {
|
|
45
|
-
return headerHeight + 48 + buffer;
|
|
46
|
-
}
|
|
32
|
+
function getAnchorScrollOffset() {
|
|
33
|
+
const hasTabs = document.body.classList.contains('has-tabs');
|
|
34
|
+
const headerHeight = 64;
|
|
35
|
+
const tabBarHeight = hasTabs ? 48 : 0;
|
|
36
|
+
const buffer = 24;
|
|
47
37
|
|
|
48
|
-
|
|
38
|
+
if (window.innerWidth > 1024 && window.innerWidth <= 1280) {
|
|
39
|
+
return headerHeight + 48 + buffer;
|
|
49
40
|
}
|
|
50
41
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
await navigator.clipboard.writeText(text);
|
|
55
|
-
this.showFeedback(anchor, true);
|
|
56
|
-
} catch (err) {
|
|
57
|
-
this.fallbackCopyToClipboard(text);
|
|
58
|
-
this.showFeedback(anchor, true);
|
|
59
|
-
}
|
|
42
|
+
if (window.innerWidth <= 1024) {
|
|
43
|
+
return headerHeight + 48 + buffer;
|
|
60
44
|
}
|
|
61
45
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
46
|
+
return headerHeight + tabBarHeight + buffer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function copyAnchorToClipboard(text, anchor) {
|
|
50
|
+
try {
|
|
51
|
+
await navigator.clipboard.writeText(text);
|
|
52
|
+
showAnchorFeedback(anchor, true);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
fallbackAnchorCopy(text);
|
|
55
|
+
showAnchorFeedback(anchor, true);
|
|
72
56
|
}
|
|
57
|
+
}
|
|
73
58
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
59
|
+
function fallbackAnchorCopy(text) {
|
|
60
|
+
const textarea = document.createElement('textarea');
|
|
61
|
+
textarea.value = text;
|
|
62
|
+
textarea.style.position = 'fixed';
|
|
63
|
+
textarea.style.opacity = '0';
|
|
64
|
+
document.body.appendChild(textarea);
|
|
65
|
+
textarea.select();
|
|
66
|
+
document.execCommand('copy');
|
|
67
|
+
document.body.removeChild(textarea);
|
|
68
|
+
}
|
|
78
69
|
|
|
79
|
-
|
|
70
|
+
function showAnchorFeedback(anchor, success) {
|
|
71
|
+
const originalTitle = anchor.getAttribute('aria-label');
|
|
72
|
+
anchor.setAttribute('aria-label', success ? 'Link copied!' : 'Failed to copy');
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
anchor.style.color = success ? 'var(--color-success, #10b981)' : 'var(--color-danger, #ef4444)';
|
|
75
|
+
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
anchor.setAttribute('aria-label', originalTitle);
|
|
78
|
+
anchor.style.color = '';
|
|
79
|
+
}, 2000);
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
if (typeof window !== 'undefined') {
|
|
89
|
-
document.
|
|
90
|
-
|
|
91
|
-
}
|
|
83
|
+
if (document.readyState === 'loading') {
|
|
84
|
+
document.addEventListener('DOMContentLoaded', function() { initializeHeadingAnchors(); });
|
|
85
|
+
} else {
|
|
86
|
+
initializeHeadingAnchors();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
window.docyard = window.docyard || {};
|
|
90
|
+
window.docyard.initHeadingAnchors = initializeHeadingAnchors;
|
|
92
91
|
}
|
|
@@ -49,8 +49,9 @@
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function initLightbox() {
|
|
53
|
-
var
|
|
52
|
+
function initLightbox(root) {
|
|
53
|
+
var container = root || document;
|
|
54
|
+
var contentImages = container.querySelectorAll('.content img');
|
|
54
55
|
|
|
55
56
|
contentImages.forEach(function(img) {
|
|
56
57
|
if (img.hasAttribute('data-no-zoom')) {
|
|
@@ -58,6 +59,9 @@
|
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
if (img.hasAttribute('data-lightbox-initialized')) return;
|
|
63
|
+
img.setAttribute('data-lightbox-initialized', 'true');
|
|
64
|
+
|
|
61
65
|
img.addEventListener('click', function() {
|
|
62
66
|
openLightbox(this.src, this.alt);
|
|
63
67
|
});
|
|
@@ -65,8 +69,11 @@
|
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
if (document.readyState === 'loading') {
|
|
68
|
-
document.addEventListener('DOMContentLoaded', initLightbox);
|
|
72
|
+
document.addEventListener('DOMContentLoaded', function() { initLightbox(); });
|
|
69
73
|
} else {
|
|
70
74
|
initLightbox();
|
|
71
75
|
}
|
|
76
|
+
|
|
77
|
+
window.docyard = window.docyard || {};
|
|
78
|
+
window.docyard.initLightbox = initLightbox;
|
|
72
79
|
})();
|
|
@@ -304,20 +304,25 @@ class TabsManager {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
function initializeTabs() {
|
|
308
|
-
const tabsContainers =
|
|
307
|
+
function initializeTabs(root = document) {
|
|
308
|
+
const tabsContainers = root.querySelectorAll('.docyard-tabs');
|
|
309
309
|
|
|
310
310
|
tabsContainers.forEach(container => {
|
|
311
|
+
if (container.hasAttribute('data-tabs-initialized')) return;
|
|
312
|
+
container.setAttribute('data-tabs-initialized', 'true');
|
|
311
313
|
new TabsManager(container);
|
|
312
314
|
});
|
|
313
315
|
}
|
|
314
316
|
|
|
315
317
|
if (document.readyState === 'loading') {
|
|
316
|
-
document.addEventListener('DOMContentLoaded', initializeTabs);
|
|
318
|
+
document.addEventListener('DOMContentLoaded', function() { initializeTabs(); });
|
|
317
319
|
} else {
|
|
318
320
|
initializeTabs();
|
|
319
321
|
}
|
|
320
322
|
|
|
323
|
+
window.docyard = window.docyard || {};
|
|
324
|
+
window.docyard.initTabs = initializeTabs;
|
|
325
|
+
|
|
321
326
|
if (typeof module !== 'undefined' && module.exports) {
|
|
322
327
|
module.exports = { TabsManager };
|
|
323
328
|
}
|
|
@@ -1,35 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
var tooltipPopover = null;
|
|
2
|
+
var tooltipHideTimeout = null;
|
|
3
|
+
var tooltipIsHoveringPopover = false;
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
function initializeTooltips(root = document) {
|
|
6
|
+
const tooltips = root.querySelectorAll('.docyard-tooltip');
|
|
7
|
+
if (tooltips.length === 0) return;
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
if (!tooltipPopover) {
|
|
10
|
+
tooltipPopover = createTooltipPopover();
|
|
11
|
+
document.body.appendChild(tooltipPopover);
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
tooltipPopover.addEventListener('mouseenter', () => {
|
|
14
|
+
tooltipIsHoveringPopover = true;
|
|
15
|
+
clearTimeout(tooltipHideTimeout);
|
|
16
|
+
}, { passive: true });
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
tooltipPopover.addEventListener('mouseleave', () => {
|
|
19
|
+
tooltipIsHoveringPopover = false;
|
|
20
|
+
tooltipHideTimeout = setTimeout(() => {
|
|
21
|
+
hideTooltipPopover(tooltipPopover);
|
|
22
|
+
}, 100);
|
|
23
|
+
}, { passive: true });
|
|
24
|
+
}
|
|
22
25
|
|
|
23
26
|
tooltips.forEach(tooltip => {
|
|
27
|
+
if (tooltip.hasAttribute('data-tooltip-initialized')) return;
|
|
28
|
+
tooltip.setAttribute('data-tooltip-initialized', 'true');
|
|
29
|
+
|
|
24
30
|
tooltip.addEventListener('mouseenter', () => {
|
|
25
|
-
clearTimeout(
|
|
26
|
-
showTooltipPopover(
|
|
31
|
+
clearTimeout(tooltipHideTimeout);
|
|
32
|
+
showTooltipPopover(tooltipPopover, tooltip);
|
|
27
33
|
}, { passive: true });
|
|
28
34
|
|
|
29
35
|
tooltip.addEventListener('mouseleave', () => {
|
|
30
|
-
|
|
31
|
-
if (!
|
|
32
|
-
hideTooltipPopover(
|
|
36
|
+
tooltipHideTimeout = setTimeout(() => {
|
|
37
|
+
if (!tooltipIsHoveringPopover) {
|
|
38
|
+
hideTooltipPopover(tooltipPopover);
|
|
33
39
|
}
|
|
34
40
|
}, 100);
|
|
35
41
|
}, { passive: true });
|
|
@@ -112,7 +118,10 @@ function hideTooltipPopover(popover) {
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
if (document.readyState === 'loading') {
|
|
115
|
-
document.addEventListener('DOMContentLoaded', initializeTooltips);
|
|
121
|
+
document.addEventListener('DOMContentLoaded', function() { initializeTooltips(); });
|
|
116
122
|
} else {
|
|
117
123
|
initializeTooltips();
|
|
118
124
|
}
|
|
125
|
+
|
|
126
|
+
window.docyard = window.docyard || {};
|
|
127
|
+
window.docyard.initTooltips = initializeTooltips;
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
if (data.type === 'content') {
|
|
11
11
|
console.log('[Docyard] Content updated');
|
|
12
12
|
reloadContent();
|
|
13
|
+
} else if (data.type === 'css') {
|
|
14
|
+
console.log('[Docyard] CSS updated');
|
|
15
|
+
reloadStyles();
|
|
13
16
|
} else {
|
|
14
17
|
console.log('[Docyard] Full reload');
|
|
15
18
|
location.reload();
|
|
@@ -20,6 +23,44 @@
|
|
|
20
23
|
eventSource.close();
|
|
21
24
|
};
|
|
22
25
|
|
|
26
|
+
function reloadStyles() {
|
|
27
|
+
var links = document.querySelectorAll('link[rel="stylesheet"]');
|
|
28
|
+
var cacheBuster = Date.now();
|
|
29
|
+
|
|
30
|
+
links.forEach(function (link) {
|
|
31
|
+
var href = link.getAttribute('href');
|
|
32
|
+
if (!href) return;
|
|
33
|
+
|
|
34
|
+
var baseHref = href.split('?')[0];
|
|
35
|
+
var newHref = baseHref + '?_dc=' + cacheBuster;
|
|
36
|
+
|
|
37
|
+
var newLink = document.createElement('link');
|
|
38
|
+
newLink.rel = 'stylesheet';
|
|
39
|
+
newLink.href = newHref;
|
|
40
|
+
|
|
41
|
+
newLink.onload = function () {
|
|
42
|
+
link.remove();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
link.parentNode.insertBefore(newLink, link.nextSibling);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function reinitializeComponents(container) {
|
|
50
|
+
if (window.Prism) window.Prism.highlightAll();
|
|
51
|
+
if (window.docyardTOC) window.docyardTOC.init();
|
|
52
|
+
|
|
53
|
+
var docyard = window.docyard || {};
|
|
54
|
+
if (docyard.initTabs) docyard.initTabs(container);
|
|
55
|
+
if (docyard.initFileTrees) docyard.initFileTrees(container);
|
|
56
|
+
if (docyard.initCodeGroups) docyard.initCodeGroups(container);
|
|
57
|
+
if (docyard.initCodeBlocks) docyard.initCodeBlocks(container);
|
|
58
|
+
if (docyard.initHeadingAnchors) docyard.initHeadingAnchors(container);
|
|
59
|
+
if (docyard.initAbbreviations) docyard.initAbbreviations(container);
|
|
60
|
+
if (docyard.initLightbox) docyard.initLightbox(container);
|
|
61
|
+
if (docyard.initTooltips) docyard.initTooltips(container);
|
|
62
|
+
}
|
|
63
|
+
|
|
23
64
|
function reloadContent() {
|
|
24
65
|
var cacheBuster = '_dc=' + Date.now();
|
|
25
66
|
var separator = location.href.indexOf('?') === -1 ? '?' : '&';
|
|
@@ -33,8 +74,7 @@
|
|
|
33
74
|
|
|
34
75
|
if (newContent && currentContent) {
|
|
35
76
|
currentContent.innerHTML = newContent.innerHTML;
|
|
36
|
-
|
|
37
|
-
if (window.docyardTOC) window.docyardTOC.init();
|
|
77
|
+
reinitializeComponents(currentContent);
|
|
38
78
|
updateErrorOverlay(newDoc);
|
|
39
79
|
} else {
|
|
40
80
|
location.reload();
|