ligarb 0.9.1 → 0.10.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/assets/mermaid_check.mjs +5 -1
- data/assets/style.css +16 -0
- data/docs/help.md +17 -0
- data/lib/ligarb/builder.rb +23 -2
- data/lib/ligarb/chapter.rb +1 -1
- data/lib/ligarb/config.rb +3 -0
- data/lib/ligarb/template.rb +4 -1
- data/lib/ligarb/version.rb +1 -1
- data/templates/book.html.erb +18 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ba0a513e610d71f30b92bf61b42e2acdab5516e6105b56a43603692c60a3a9a
|
|
4
|
+
data.tar.gz: 5824b715da62d8d568d597a2edc0dc9a85dc0b7b7ef028499d7ce9cbcb65d161
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c58b836062f00ef965e59f44fef837fe0551d22a57f93f957b0a25d718b18ef79447b11b50f8c4a52540fec9d96656397266d4500ecacc60e42faa474099abd3
|
|
7
|
+
data.tar.gz: f74f174bc842665f9d68387381cd92cfc9ee48745f1bf339d3f6393e96b17a7ddec0adbd561f8e56cfc92fd6a6b9eac6d3b102e4abc1e9f071407baa7870bbd2
|
data/assets/mermaid_check.mjs
CHANGED
|
@@ -47,7 +47,11 @@ function makeStub(name, overrides = {}) {
|
|
|
47
47
|
globalThis.window = globalThis;
|
|
48
48
|
// nodeType 9 = DOCUMENT_NODE; DOMPurify checks it to decide it has a real DOM.
|
|
49
49
|
globalThis.document = makeStub("document", { nodeType: 9 });
|
|
50
|
-
|
|
50
|
+
// Node >= 21 ships a built-in read-only `navigator` global; assigning to it
|
|
51
|
+
// throws a TypeError. Only define our stub when the runtime lacks one.
|
|
52
|
+
if (!("navigator" in globalThis)) {
|
|
53
|
+
globalThis.navigator = { userAgent: "node" };
|
|
54
|
+
}
|
|
51
55
|
globalThis.addEventListener = noop;
|
|
52
56
|
globalThis.location = { href: "http://localhost/", protocol: "http:" };
|
|
53
57
|
globalThis.Element = function Element() {};
|
data/assets/style.css
CHANGED
|
@@ -77,6 +77,22 @@ body {
|
|
|
77
77
|
opacity: 1;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
.ai-md-hint {
|
|
81
|
+
margin: 0 0 1.5rem;
|
|
82
|
+
font-size: 0.8rem;
|
|
83
|
+
color: var(--color-text-muted);
|
|
84
|
+
opacity: 0.8;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.ai-md-hint a {
|
|
88
|
+
color: inherit;
|
|
89
|
+
text-decoration: underline;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@media print {
|
|
93
|
+
.ai-md-hint { display: none; }
|
|
94
|
+
}
|
|
95
|
+
|
|
80
96
|
.book-title {
|
|
81
97
|
font-size: 1.1rem;
|
|
82
98
|
font-weight: 700;
|
data/docs/help.md
CHANGED
|
@@ -83,6 +83,23 @@ The output directory contains index.html plus js/ and css/ subdirectories
|
|
|
83
83
|
for auto-downloaded libraries (highlight.js, mermaid, KaTeX, etc.).
|
|
84
84
|
Open index.html directly in a browser — no web server needed.
|
|
85
85
|
|
|
86
|
+
The build also emits a Markdown full-text version of the book next to
|
|
87
|
+
index.html, intended for AI/LLM readers (HTML is noisy and costly to read;
|
|
88
|
+
clean Markdown is cheaper and more faithful). For a single-language build it
|
|
89
|
+
is `index.md`; for a multi-language (hub) build it is `index.<lang>.md` per
|
|
90
|
+
language. Each is the concatenated chapter sources in reading order. The HTML
|
|
91
|
+
points to it two ways: a small visible note at the top of the content ("For
|
|
92
|
+
AI/LLM readers: a clean Markdown full-text version of this book is at …") and
|
|
93
|
+
a `<link rel="alternate" type="text/markdown">` in the head. Both use a
|
|
94
|
+
relative href (the bare filename), so they resolve against whatever URL the
|
|
95
|
+
page is actually served from — local preview, staging, or production — rather
|
|
96
|
+
than hard-coding `site_url` (the absolute canonical URL still lives in
|
|
97
|
+
`og:url` / `<link rel="canonical">`). A fetcher resolves it against the page's
|
|
98
|
+
own URL, so a model that fetched the page finds the bundle next to it. No tool
|
|
99
|
+
is guaranteed to honor these automatically —
|
|
100
|
+
the visible note is what actually reaches a model that fetched the page,
|
|
101
|
+
since the in-page text is included in what the fetcher hands to the model.
|
|
102
|
+
|
|
86
103
|
When `github_review.enabled` is true and `repository` is set (see
|
|
87
104
|
Configuration below), the build also injects a static "Report as issue"
|
|
88
105
|
feedback UI: readers select text, pick a type and add a comment, and a
|
data/lib/ligarb/builder.rb
CHANGED
|
@@ -41,13 +41,18 @@ module Ligarb
|
|
|
41
41
|
|
|
42
42
|
bibliography = resolve_citations!(all_chapters)
|
|
43
43
|
|
|
44
|
+
FileUtils.mkdir_p(@config.output_path)
|
|
45
|
+
|
|
46
|
+
md_name = "index.md"
|
|
47
|
+
write_markdown_bundle(all_chapters, File.join(@config.output_path, md_name))
|
|
48
|
+
|
|
44
49
|
html = Template.new.render(config: @config, chapters: all_chapters,
|
|
45
50
|
structure: structure, assets: assets,
|
|
46
51
|
index_entries: index_entries,
|
|
47
52
|
bibliography: bibliography,
|
|
48
|
-
github_review: github_review_data(@config)
|
|
53
|
+
github_review: github_review_data(@config),
|
|
54
|
+
ai_md: md_name)
|
|
49
55
|
|
|
50
|
-
FileUtils.mkdir_p(@config.output_path)
|
|
51
56
|
output_file = File.join(@config.output_path, "index.html")
|
|
52
57
|
File.write(output_file, html)
|
|
53
58
|
|
|
@@ -76,6 +81,14 @@ module Ligarb
|
|
|
76
81
|
all_lang_chapters.concat(lang_data[:chapters])
|
|
77
82
|
end
|
|
78
83
|
|
|
84
|
+
FileUtils.mkdir_p(output_path)
|
|
85
|
+
|
|
86
|
+
langs.each do |ld|
|
|
87
|
+
md_name = "index.#{ld[:lang]}.md"
|
|
88
|
+
write_markdown_bundle(ld[:chapters], File.join(output_path, md_name))
|
|
89
|
+
ld[:ai_md] = md_name
|
|
90
|
+
end
|
|
91
|
+
|
|
79
92
|
assets = AssetManager.new(output_path)
|
|
80
93
|
assets.detect(all_lang_chapters)
|
|
81
94
|
assets.provision!
|
|
@@ -428,6 +441,14 @@ module Ligarb
|
|
|
428
441
|
text.to_s.gsub("&", "&").gsub("<", "<").gsub(">", ">")
|
|
429
442
|
end
|
|
430
443
|
|
|
444
|
+
# Emit a clean Markdown full-text version of the book alongside index.html.
|
|
445
|
+
# AI/LLM readers (and the in-page hint) point here for low-noise content.
|
|
446
|
+
# The bundle is the concatenated chapter sources in flat reading order.
|
|
447
|
+
def write_markdown_bundle(chapters, path)
|
|
448
|
+
body = chapters.map { |ch| ch.source.strip }.reject(&:empty?).join("\n\n")
|
|
449
|
+
File.write(path, body.empty? ? body : "#{body}\n")
|
|
450
|
+
end
|
|
451
|
+
|
|
431
452
|
def copy_images
|
|
432
453
|
images_dir = File.join(@config.base_dir, "images")
|
|
433
454
|
return unless Dir.exist?(images_dir)
|
data/lib/ligarb/chapter.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Ligarb
|
|
|
8
8
|
class CrossReferenceError < StandardError; end
|
|
9
9
|
|
|
10
10
|
attr_reader :title, :slug, :html, :headings, :number, :appendix_letter, :index_entries, :cite_entries,
|
|
11
|
-
:path, :mermaid_blocks
|
|
11
|
+
:path, :mermaid_blocks, :source
|
|
12
12
|
attr_accessor :part_title, :cover, :relative_path
|
|
13
13
|
|
|
14
14
|
Heading = Struct.new(:level, :text, :id, :display_text, keyword_init: true)
|
data/lib/ligarb/config.rb
CHANGED
|
@@ -233,6 +233,9 @@ module Ligarb
|
|
|
233
233
|
|
|
234
234
|
def load_translations_hub(data)
|
|
235
235
|
@title = data.fetch("title", "")
|
|
236
|
+
# The hub writes its combined build here; serve needs output_path to work
|
|
237
|
+
# even though hub init returns before the normal output_dir assignment.
|
|
238
|
+
@output_dir = data.fetch("output_dir", "build")
|
|
236
239
|
@translations = []
|
|
237
240
|
|
|
238
241
|
trans = data["translations"]
|
data/lib/ligarb/template.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Ligarb
|
|
|
13
13
|
@css_path = File.join(ASSETS_DIR, "style.css")
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def render(config:, chapters:, structure:, assets:, index_entries: [], bibliography: [], github_review: nil)
|
|
16
|
+
def render(config:, chapters:, structure:, assets:, index_entries: [], bibliography: [], github_review: nil, ai_md: nil)
|
|
17
17
|
css = File.read(@css_path)
|
|
18
18
|
template = File.read(@template_path)
|
|
19
19
|
|
|
@@ -42,6 +42,7 @@ module Ligarb
|
|
|
42
42
|
b.local_variable_set(:og_description, og_description(config, chapters))
|
|
43
43
|
b.local_variable_set(:og_locale, og_locale(config.language))
|
|
44
44
|
b.local_variable_set(:og_url, og_url(config))
|
|
45
|
+
b.local_variable_set(:ai_md, ai_md)
|
|
45
46
|
|
|
46
47
|
ERB.new(template, trim_mode: "-").result(b)
|
|
47
48
|
end
|
|
@@ -74,6 +75,7 @@ module Ligarb
|
|
|
74
75
|
footer: cfg.effective_footer,
|
|
75
76
|
index_tree: build_index_tree(ld[:index_entries], ld[:chapters]),
|
|
76
77
|
bibliography: ld[:bibliography],
|
|
78
|
+
ai_md: ld[:ai_md],
|
|
77
79
|
}
|
|
78
80
|
end
|
|
79
81
|
|
|
@@ -99,6 +101,7 @@ module Ligarb
|
|
|
99
101
|
b.local_variable_set(:og_description, og_description(first_config, first[:chapters]))
|
|
100
102
|
b.local_variable_set(:og_locale, og_locale(first_config.language))
|
|
101
103
|
b.local_variable_set(:og_url, og_url(first_config))
|
|
104
|
+
b.local_variable_set(:ai_md, nil)
|
|
102
105
|
|
|
103
106
|
ERB.new(template, trim_mode: "-").result(b)
|
|
104
107
|
end
|
data/lib/ligarb/version.rb
CHANGED
data/templates/book.html.erb
CHANGED
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
<meta property="og:description" content="<%= h(og_description) %>">
|
|
18
18
|
<%- end -%>
|
|
19
19
|
<meta name="twitter:card" content="summary">
|
|
20
|
+
<%- if multilang -%>
|
|
21
|
+
<%- langs.each do |ld| -%>
|
|
22
|
+
<%- if ld[:ai_md] -%>
|
|
23
|
+
<link rel="alternate" type="text/markdown" hreflang="<%= h(ld[:language]) %>" href="<%= h(ld[:ai_md]) %>" title="<%= h(ld[:title]) %> (Markdown)">
|
|
24
|
+
<%- end -%>
|
|
25
|
+
<%- end -%>
|
|
26
|
+
<%- elsif ai_md -%>
|
|
27
|
+
<link rel="alternate" type="text/markdown" href="<%= h(ai_md) %>" title="Markdown version">
|
|
28
|
+
<%- end -%>
|
|
20
29
|
<%- if author && !author.to_s.empty? -%>
|
|
21
30
|
<meta property="book:author" content="<%= h(author) %>">
|
|
22
31
|
<%- end -%>
|
|
@@ -272,6 +281,15 @@
|
|
|
272
281
|
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="Toggle sidebar">☰</button>
|
|
273
282
|
|
|
274
283
|
<main class="content" id="content">
|
|
284
|
+
<%- if multilang -%>
|
|
285
|
+
<%- langs.each do |ld| -%>
|
|
286
|
+
<%- if ld[:ai_md] -%>
|
|
287
|
+
<p class="ai-md-hint lang-content" data-lang="<%= ld[:lang] %>">For AI/LLM readers: a clean Markdown full-text version of this book is at <a href="<%= h(ld[:ai_md]) %>"><%= h(ld[:ai_md]) %></a>.</p>
|
|
288
|
+
<%- end -%>
|
|
289
|
+
<%- end -%>
|
|
290
|
+
<%- elsif ai_md -%>
|
|
291
|
+
<p class="ai-md-hint">For AI/LLM readers: a clean Markdown full-text version of this book is at <a href="<%= h(ai_md) %>"><%= h(ai_md) %></a>.</p>
|
|
292
|
+
<%- end -%>
|
|
275
293
|
<%- if multilang -%>
|
|
276
294
|
<%- langs.each do |ld| -%>
|
|
277
295
|
<%- ld[:chapters].select(&:cover?).each do |chapter| -%>
|