ligarb 0.3.0 → 0.4.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/style.css +28 -0
- data/lib/ligarb/asset_manager.rb +1 -1
- data/lib/ligarb/chapter.rb +19 -0
- data/lib/ligarb/cli.rb +71 -3
- data/lib/ligarb/config.rb +15 -1
- data/lib/ligarb/template.rb +2 -0
- data/lib/ligarb/version.rb +1 -1
- data/lib/ligarb/writer.rb +173 -0
- data/templates/book.html.erb +17 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab265966761408e494e47dbc0e00d188d86d99d881be30d5055af5d2abe29ef0
|
|
4
|
+
data.tar.gz: ecca92d4ca8f601b2c24be43694b92b5f56b37e1787f3c84540e37f2d111551f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c9be72721b4753bcd00ac2bd5125bc7e4c5361f8c4773ca0c7183a4a0ca5ae80a663d82ac231a0e5b64d70e79f16f7af9de3367a857207d840cb7cf4f5efb6a
|
|
7
|
+
data.tar.gz: 70a5189e3b2758b9a1c145ec34502a6402ec7b72f79454f7ed1bf56cfc0881d38a9f56fffbb7b5d96259b0e18151a5cf2dcaf4649ea97f1bb6ff0f49e0636c7d
|
data/assets/style.css
CHANGED
|
@@ -670,6 +670,34 @@ mark.search-highlight {
|
|
|
670
670
|
text-decoration: underline;
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
+
/* === AI Generated Notice === */
|
|
674
|
+
.ai-badge {
|
|
675
|
+
display: inline-block;
|
|
676
|
+
font-size: 0.7rem;
|
|
677
|
+
font-weight: 600;
|
|
678
|
+
color: #ca8a04;
|
|
679
|
+
background: #fefce8;
|
|
680
|
+
border: 1px solid #facc15;
|
|
681
|
+
border-radius: 3px;
|
|
682
|
+
padding: 0.1rem 0.4rem;
|
|
683
|
+
margin-top: 0.3rem;
|
|
684
|
+
letter-spacing: 0.03em;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
[data-theme="dark"] .ai-badge {
|
|
688
|
+
color: #fbbf24;
|
|
689
|
+
background: #2e2a1a;
|
|
690
|
+
border-color: #854d0e;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.chapter-footer {
|
|
694
|
+
margin-top: 2rem;
|
|
695
|
+
padding: 0.5rem 0.75rem;
|
|
696
|
+
font-size: 0.8rem;
|
|
697
|
+
color: var(--color-text-muted);
|
|
698
|
+
border-top: 1px solid var(--color-border);
|
|
699
|
+
}
|
|
700
|
+
|
|
673
701
|
/* === Print === */
|
|
674
702
|
@media print {
|
|
675
703
|
.sidebar,
|
data/lib/ligarb/asset_manager.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Ligarb
|
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
katex: {
|
|
25
|
-
fence_pattern: /class="math-block"/,
|
|
25
|
+
fence_pattern: /class="math-(block|inline)"/,
|
|
26
26
|
files: {
|
|
27
27
|
"js/katex.min.js" => "https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.js",
|
|
28
28
|
"css/katex.min.css" => "https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.css",
|
data/lib/ligarb/chapter.rb
CHANGED
|
@@ -101,6 +101,7 @@ module Ligarb
|
|
|
101
101
|
@html = rewrite_image_paths(doc.to_html)
|
|
102
102
|
@html = apply_heading_ids(@html)
|
|
103
103
|
@html = convert_special_code_blocks(@html)
|
|
104
|
+
@html = convert_inline_math(@html)
|
|
104
105
|
@html = convert_admonitions(@html)
|
|
105
106
|
@html = scope_footnote_ids(@html)
|
|
106
107
|
@index_entries = []
|
|
@@ -172,6 +173,24 @@ module Ligarb
|
|
|
172
173
|
end
|
|
173
174
|
end
|
|
174
175
|
|
|
176
|
+
def convert_inline_math(html)
|
|
177
|
+
# Protect <pre>...</pre> and <code>...</code> from conversion
|
|
178
|
+
placeholders = []
|
|
179
|
+
protected = html.gsub(%r{<(pre|code)([ >])(.*?)</\1>}m) do
|
|
180
|
+
placeholders << $&
|
|
181
|
+
"\x00PROTECT#{placeholders.size - 1}\x00"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Convert $...$ to inline math (exclude $$, and $ followed/preceded by space)
|
|
185
|
+
result = protected.gsub(/(?<!\$)\$(?!\$)(?!\s)(.+?)(?<!\s)(?<!\$)\$(?!\$)/m) do
|
|
186
|
+
raw = decode_entities($1)
|
|
187
|
+
%(<span class="math-inline" data-math="#{encode_attr(raw)}"></span>)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Restore protected parts
|
|
191
|
+
result.gsub(/\x00PROTECT(\d+)\x00/) { placeholders[$1.to_i] }
|
|
192
|
+
end
|
|
193
|
+
|
|
175
194
|
def decode_entities(text)
|
|
176
195
|
text.gsub("&", "&").gsub("<", "<").gsub(">", ">").gsub(""", '"').gsub("'", "'")
|
|
177
196
|
end
|
data/lib/ligarb/cli.rb
CHANGED
|
@@ -17,6 +17,16 @@ module Ligarb
|
|
|
17
17
|
Builder.new(config_path).build
|
|
18
18
|
when "init"
|
|
19
19
|
Initializer.new(args.first).run
|
|
20
|
+
when "write"
|
|
21
|
+
if args.delete("--init")
|
|
22
|
+
require_relative "writer"
|
|
23
|
+
Writer.init_brief(args.first)
|
|
24
|
+
else
|
|
25
|
+
brief_path = args.reject { |a| a.start_with?("--") }.first || "brief.yml"
|
|
26
|
+
no_build = args.include?("--no-build")
|
|
27
|
+
require_relative "writer"
|
|
28
|
+
Writer.new(brief_path, no_build: no_build).run
|
|
29
|
+
end
|
|
20
30
|
when "--help", "-h", nil
|
|
21
31
|
print_usage
|
|
22
32
|
when "help"
|
|
@@ -37,6 +47,8 @@ module Ligarb
|
|
|
37
47
|
Usage:
|
|
38
48
|
ligarb init [DIRECTORY] Create a new book project
|
|
39
49
|
ligarb build [CONFIG] Build the HTML book (default CONFIG: book.yml)
|
|
50
|
+
ligarb write [BRIEF] Generate a book with AI from brief.yml
|
|
51
|
+
ligarb write --init [DIR] Create DIR/brief.yml template
|
|
40
52
|
ligarb help Show detailed specification (for AI integration)
|
|
41
53
|
ligarb version Show version number
|
|
42
54
|
|
|
@@ -53,6 +65,8 @@ module Ligarb
|
|
|
53
65
|
chapter_numbers (optional) Show chapter/section numbers (default: true)
|
|
54
66
|
style (optional) Custom CSS file path (default: none)
|
|
55
67
|
repository (optional) GitHub repository URL for "Edit on GitHub" links
|
|
68
|
+
ai_generated (optional) Mark as AI-generated (badge + meta tags, default: false)
|
|
69
|
+
footer (optional) Custom text at bottom of each chapter
|
|
56
70
|
|
|
57
71
|
Example:
|
|
58
72
|
ligarb build
|
|
@@ -60,8 +74,8 @@ module Ligarb
|
|
|
60
74
|
USAGE
|
|
61
75
|
end
|
|
62
76
|
|
|
63
|
-
def
|
|
64
|
-
|
|
77
|
+
def spec_text
|
|
78
|
+
<<~SPEC
|
|
65
79
|
ligarb - Generate a single-page HTML book from Markdown files
|
|
66
80
|
|
|
67
81
|
Version: #{VERSION}
|
|
@@ -117,6 +131,14 @@ module Ligarb
|
|
|
117
131
|
When set, each chapter shows a "View on GitHub" link.
|
|
118
132
|
The link points to {repository}/blob/HEAD/{path-from-git-root}.
|
|
119
133
|
The chapter path is resolved relative to the Git repository root.
|
|
134
|
+
ai_generated: (optional) Mark the book as AI-generated content. Default: false.
|
|
135
|
+
When true: adds an "AI Generated" badge in the sidebar header,
|
|
136
|
+
adds a default disclaimer footer to each chapter, and adds
|
|
137
|
+
noindex/noai meta tags to prevent search indexing and AI training.
|
|
138
|
+
The footer text can be overridden with the 'footer' field.
|
|
139
|
+
footer: (optional) Custom text displayed at the bottom of each chapter.
|
|
140
|
+
Overrides the default ai_generated disclaimer if both are set.
|
|
141
|
+
Useful for copyright notices, disclaimers, or other per-chapter text.
|
|
120
142
|
chapters: (required) Book structure. An array that can contain:
|
|
121
143
|
- A cover: a centered title/landing page
|
|
122
144
|
- A string: a chapter Markdown file path (relative to book.yml)
|
|
@@ -262,6 +284,17 @@ module Ligarb
|
|
|
262
284
|
E = mc^2
|
|
263
285
|
```
|
|
264
286
|
|
|
287
|
+
Inline math uses $...$ syntax within text:
|
|
288
|
+
|
|
289
|
+
The equation $E = mc^2$ is well-known.
|
|
290
|
+
|
|
291
|
+
Rules for inline math:
|
|
292
|
+
- $$ is not matched (use ```math for display math)
|
|
293
|
+
- $ followed by a space is not matched (e.g. $10)
|
|
294
|
+
- $ preceded by a space is not matched
|
|
295
|
+
- Content inside <code> and <pre> is not affected
|
|
296
|
+
- The content is rendered with KaTeX (displayMode: false)
|
|
297
|
+
|
|
265
298
|
== Images ==
|
|
266
299
|
|
|
267
300
|
Place image files in the 'images/' directory next to book.yml.
|
|
@@ -405,8 +438,43 @@ module Ligarb
|
|
|
405
438
|
|
|
406
439
|
Each chapter displays Previous and Next navigation links at the bottom.
|
|
407
440
|
These follow the flat chapter order (including across parts and appendix).
|
|
408
|
-
|
|
441
|
+
Cover pages do not show navigation.
|
|
442
|
+
|
|
443
|
+
== Write Command ==
|
|
444
|
+
|
|
445
|
+
ligarb write [BRIEF] Generate a complete book using AI (Claude).
|
|
446
|
+
BRIEF defaults to 'brief.yml' in the current directory.
|
|
447
|
+
Reads the brief, sends a prompt to Claude, and builds
|
|
448
|
+
the generated book. Files are created in the same
|
|
449
|
+
directory as brief.yml.
|
|
450
|
+
|
|
451
|
+
ligarb write --init [DIR] Create a brief.yml template.
|
|
452
|
+
If DIR is given, creates DIR/brief.yml (mkdir as needed).
|
|
453
|
+
If omitted, creates brief.yml in the current directory.
|
|
454
|
+
|
|
455
|
+
ligarb write --no-build Generate files only, skip the build step.
|
|
456
|
+
|
|
457
|
+
brief.yml fields:
|
|
458
|
+
|
|
459
|
+
title: (required) The book title.
|
|
460
|
+
language: (optional) Language. Default: "ja".
|
|
461
|
+
audience: (optional) Target audience (used in the prompt).
|
|
462
|
+
notes: (optional) Additional instructions for Claude (free text).
|
|
463
|
+
author: (optional) Passed through to book.yml.
|
|
464
|
+
output_dir: (optional) Passed through to book.yml.
|
|
465
|
+
chapter_numbers: (optional) Passed through to book.yml.
|
|
466
|
+
style: (optional) Passed through to book.yml.
|
|
467
|
+
repository: (optional) Passed through to book.yml.
|
|
468
|
+
|
|
469
|
+
The book is generated in the directory containing brief.yml.
|
|
470
|
+
Example: 'ligarb write ruby_book/brief.yml' creates files in ruby_book/.
|
|
471
|
+
|
|
472
|
+
Requires the 'claude' CLI to be installed.
|
|
409
473
|
SPEC
|
|
410
474
|
end
|
|
475
|
+
|
|
476
|
+
def print_spec
|
|
477
|
+
puts spec_text
|
|
478
|
+
end
|
|
411
479
|
end
|
|
412
480
|
end
|
data/lib/ligarb/config.rb
CHANGED
|
@@ -13,7 +13,8 @@ module Ligarb
|
|
|
13
13
|
# children: array of StructEntry (for :part and :appendix_group)
|
|
14
14
|
|
|
15
15
|
attr_reader :title, :author, :language, :output_dir, :base_dir,
|
|
16
|
-
:chapter_numbers, :structure, :style, :repository
|
|
16
|
+
:chapter_numbers, :structure, :style, :repository,
|
|
17
|
+
:ai_generated, :footer
|
|
17
18
|
|
|
18
19
|
def initialize(path)
|
|
19
20
|
@base_dir = File.dirname(File.expand_path(path))
|
|
@@ -28,6 +29,8 @@ module Ligarb
|
|
|
28
29
|
@chapter_numbers = data.fetch("chapter_numbers", true)
|
|
29
30
|
@style = data.fetch("style", nil)
|
|
30
31
|
@repository = data.fetch("repository", nil)
|
|
32
|
+
@ai_generated = data.fetch("ai_generated", false)
|
|
33
|
+
@footer = data.fetch("footer", nil)
|
|
31
34
|
@structure = parse_structure(data["chapters"])
|
|
32
35
|
end
|
|
33
36
|
|
|
@@ -43,6 +46,17 @@ module Ligarb
|
|
|
43
46
|
@language == "ja" ? "付録" : "Appendix"
|
|
44
47
|
end
|
|
45
48
|
|
|
49
|
+
def effective_footer
|
|
50
|
+
return @footer if @footer
|
|
51
|
+
return nil unless @ai_generated
|
|
52
|
+
|
|
53
|
+
if @language == "ja"
|
|
54
|
+
"この章の内容は AI によって生成されました。正確性は保証されません。"
|
|
55
|
+
else
|
|
56
|
+
"This chapter was generated by AI. Accuracy is not guaranteed."
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
46
60
|
# Returns a flat list of all chapter file paths (excluding part title pages)
|
|
47
61
|
def chapter_paths
|
|
48
62
|
collect_chapter_paths(@structure)
|
data/lib/ligarb/template.rb
CHANGED
|
@@ -31,6 +31,8 @@ module Ligarb
|
|
|
31
31
|
b.local_variable_set(:assets, assets)
|
|
32
32
|
b.local_variable_set(:repository, config.repository)
|
|
33
33
|
b.local_variable_set(:appendix_label, config.appendix_label)
|
|
34
|
+
b.local_variable_set(:ai_generated, config.ai_generated)
|
|
35
|
+
b.local_variable_set(:footer, config.effective_footer)
|
|
34
36
|
b.local_variable_set(:index_tree, build_index_tree(index_entries, chapters))
|
|
35
37
|
|
|
36
38
|
ERB.new(template, trim_mode: "-").result(b)
|
data/lib/ligarb/version.rb
CHANGED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Ligarb
|
|
7
|
+
class Writer
|
|
8
|
+
BRIEF_FIELDS_FOR_BOOK_YML = %w[author output_dir chapter_numbers style repository].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(brief_path, no_build: false)
|
|
11
|
+
@brief_path = File.expand_path(brief_path)
|
|
12
|
+
@no_build = no_build
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
check_claude_installed!
|
|
17
|
+
brief = load_brief
|
|
18
|
+
output_dir = output_dir_for(brief)
|
|
19
|
+
book_yml_path = File.join(output_dir, "book.yml")
|
|
20
|
+
|
|
21
|
+
if File.exist?(book_yml_path)
|
|
22
|
+
$stderr.puts "Error: #{book_yml_path} already exists. Remove it first to regenerate."
|
|
23
|
+
exit 1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
FileUtils.mkdir_p(output_dir)
|
|
27
|
+
prompt = build_prompt(brief, output_dir)
|
|
28
|
+
run_claude(prompt)
|
|
29
|
+
|
|
30
|
+
unless File.exist?(book_yml_path)
|
|
31
|
+
$stderr.puts "Error: Claude did not generate book.yml in #{output_dir}"
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
puts "Book files generated in #{output_dir}"
|
|
36
|
+
|
|
37
|
+
unless @no_build
|
|
38
|
+
puts "Building..."
|
|
39
|
+
require_relative "builder"
|
|
40
|
+
Builder.new(book_yml_path).build
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.init_brief(directory = nil)
|
|
45
|
+
dir = directory || "."
|
|
46
|
+
target = File.expand_path(dir)
|
|
47
|
+
path = File.join(target, "brief.yml")
|
|
48
|
+
|
|
49
|
+
if File.exist?(path)
|
|
50
|
+
$stderr.puts "Error: #{path} already exists."
|
|
51
|
+
exit 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
FileUtils.mkdir_p(target)
|
|
55
|
+
|
|
56
|
+
File.write(path, <<~YAML)
|
|
57
|
+
# brief.yml - Book brief for ligarb write
|
|
58
|
+
title: "My Book"
|
|
59
|
+
language: ja
|
|
60
|
+
audience: ""
|
|
61
|
+
notes: |
|
|
62
|
+
5章くらいで。
|
|
63
|
+
YAML
|
|
64
|
+
|
|
65
|
+
claude_md = File.join(target, "CLAUDE.md")
|
|
66
|
+
created_claude_md = false
|
|
67
|
+
unless File.exist?(claude_md)
|
|
68
|
+
File.write(claude_md, <<~MD)
|
|
69
|
+
# ligarb book project
|
|
70
|
+
|
|
71
|
+
This is a book project using [ligarb](https://github.com/ko1/ligarb).
|
|
72
|
+
|
|
73
|
+
## Commands
|
|
74
|
+
|
|
75
|
+
- `ligarb build` — Build the book (generates build/index.html)
|
|
76
|
+
- `ligarb help` — Show full specification (Markdown syntax, config options, etc.)
|
|
77
|
+
|
|
78
|
+
## Key rules
|
|
79
|
+
|
|
80
|
+
- All chapter files are Markdown (.md), listed in book.yml
|
|
81
|
+
- The first h1 in each file is the chapter title
|
|
82
|
+
- Use ```mermaid, ```math, admonitions (> [!NOTE]), etc. as needed
|
|
83
|
+
- Run `ligarb build` after changes to verify the output
|
|
84
|
+
MD
|
|
85
|
+
created_claude_md = true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
puts "Created #{path}"
|
|
89
|
+
puts "Created #{claude_md}" if created_claude_md
|
|
90
|
+
brief_arg = directory ? " #{path}" : ""
|
|
91
|
+
puts "Edit brief.yml, then run 'ligarb write#{brief_arg}' to generate the book."
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def check_claude_installed!
|
|
97
|
+
unless system("claude", "--version", out: File::NULL, err: File::NULL)
|
|
98
|
+
$stderr.puts "Error: 'claude' command not found. Install Claude Code first."
|
|
99
|
+
exit 1
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def load_brief
|
|
104
|
+
unless File.exist?(@brief_path)
|
|
105
|
+
$stderr.puts "Error: #{@brief_path} not found."
|
|
106
|
+
exit 1
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
brief = YAML.safe_load_file(@brief_path)
|
|
110
|
+
unless brief.is_a?(Hash) && brief["title"] && !brief["title"].empty?
|
|
111
|
+
$stderr.puts "Error: 'title' is required in #{@brief_path}."
|
|
112
|
+
exit 1
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
brief
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def output_dir_for(_brief)
|
|
119
|
+
File.dirname(@brief_path)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def build_prompt(brief, output_dir)
|
|
123
|
+
abs_output_dir = File.expand_path(output_dir)
|
|
124
|
+
spec = CLI.spec_text
|
|
125
|
+
|
|
126
|
+
lines = []
|
|
127
|
+
lines << "You are writing a book using the ligarb tool."
|
|
128
|
+
lines << ""
|
|
129
|
+
lines << "<ligarb-spec>"
|
|
130
|
+
lines << spec
|
|
131
|
+
lines << "</ligarb-spec>"
|
|
132
|
+
lines << ""
|
|
133
|
+
lines << "Write a complete book based on this brief:"
|
|
134
|
+
lines << "- Title: #{brief["title"]}"
|
|
135
|
+
lines << "- Language: #{brief["language"] || "ja"}"
|
|
136
|
+
lines << "- Target audience: #{brief["audience"]}" if brief["audience"] && !brief["audience"].empty?
|
|
137
|
+
|
|
138
|
+
if brief["notes"] && !brief["notes"].strip.empty?
|
|
139
|
+
lines << ""
|
|
140
|
+
lines << "Additional instructions:"
|
|
141
|
+
lines << brief["notes"].strip
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
book_yml_fields = BRIEF_FIELDS_FOR_BOOK_YML.select { |k| brief.key?(k) }
|
|
145
|
+
if book_yml_fields.any?
|
|
146
|
+
settings = book_yml_fields.map { |k| "#{k}: #{brief[k].inspect}" }.join(", ")
|
|
147
|
+
lines << ""
|
|
148
|
+
lines << "In book.yml, set: #{settings}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
lines << ""
|
|
152
|
+
lines << "Create all files in: #{abs_output_dir}"
|
|
153
|
+
lines << "In book.yml, always set: ai_generated: true"
|
|
154
|
+
lines << "Create book.yml first, then each chapter .md file."
|
|
155
|
+
lines << "Include a cover page for books with 4+ chapters."
|
|
156
|
+
lines << "Each chapter: substantive content with multiple ## sections."
|
|
157
|
+
lines << "Use code blocks, admonitions, mermaid diagrams where appropriate."
|
|
158
|
+
lines << "Chapter filenames: 01-topic.md, 02-topic.md, etc."
|
|
159
|
+
|
|
160
|
+
lines.join("\n")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def run_claude(prompt)
|
|
164
|
+
tools = "Write,Bash,WebFetch,WebSearch"
|
|
165
|
+
allowed = "Write,Bash(mkdir:*),Bash(ls:*),Bash(ligarb:*),WebFetch,WebSearch"
|
|
166
|
+
cmd = ["claude", "-p", "--verbose", prompt, "--tools", tools, "--allowedTools", allowed]
|
|
167
|
+
unless system(*cmd)
|
|
168
|
+
$stderr.puts "Error: Claude process failed."
|
|
169
|
+
exit 1
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
data/templates/book.html.erb
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title><%= title %></title>
|
|
7
|
+
<%- if ai_generated -%>
|
|
8
|
+
<meta name="robots" content="noindex, nofollow, noarchive">
|
|
9
|
+
<meta name="robots" content="noai, noimageai">
|
|
10
|
+
<%- end -%>
|
|
7
11
|
<style>
|
|
8
12
|
<%= css %>
|
|
9
13
|
</style>
|
|
@@ -35,6 +39,9 @@
|
|
|
35
39
|
<%- unless author.empty? -%>
|
|
36
40
|
<p class="book-author"><%= author %></p>
|
|
37
41
|
<%- end -%>
|
|
42
|
+
<%- if ai_generated -%>
|
|
43
|
+
<span class="ai-badge"><%= language == 'ja' ? 'AI 生成' : 'AI Generated' %></span>
|
|
44
|
+
<%- end -%>
|
|
38
45
|
</div>
|
|
39
46
|
<div class="search-box">
|
|
40
47
|
<input type="text" id="toc-search" placeholder="Search..." autocomplete="off">
|
|
@@ -123,7 +130,10 @@
|
|
|
123
130
|
<a href="<%= repository.chomp('/') %>/blob/HEAD/<%= chapter.relative_path %>" target="_blank" rel="noopener">View on GitHub</a>
|
|
124
131
|
</div>
|
|
125
132
|
<%- end -%>
|
|
126
|
-
<%-
|
|
133
|
+
<%- if footer && !chapter.part_title? && !chapter.cover? -%>
|
|
134
|
+
<div class="chapter-footer"><%= footer %></div>
|
|
135
|
+
<%- end -%>
|
|
136
|
+
<%- unless chapter.cover? -%>
|
|
127
137
|
<nav class="chapter-nav">
|
|
128
138
|
<%- if idx > 0 -%>
|
|
129
139
|
<a href="#" class="nav-prev" onclick="showChapter('<%= chapters[idx-1].slug %>'); return false;">← <%= chapters[idx-1].display_title %></a>
|
|
@@ -211,6 +221,12 @@
|
|
|
211
221
|
catch(e) { el.textContent = el.getAttribute('data-math'); }
|
|
212
222
|
}
|
|
213
223
|
});
|
|
224
|
+
section.querySelectorAll('.math-inline[data-math]').forEach(function(el) {
|
|
225
|
+
if (el.childNodes.length === 0) {
|
|
226
|
+
try { katex.render(el.getAttribute('data-math'), el, {displayMode: false, throwOnError: false}); }
|
|
227
|
+
catch(e) { el.textContent = el.getAttribute('data-math'); }
|
|
228
|
+
}
|
|
229
|
+
});
|
|
214
230
|
}
|
|
215
231
|
}
|
|
216
232
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ligarb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ligarb contributors
|
|
@@ -82,6 +82,7 @@ files:
|
|
|
82
82
|
- lib/ligarb/initializer.rb
|
|
83
83
|
- lib/ligarb/template.rb
|
|
84
84
|
- lib/ligarb/version.rb
|
|
85
|
+
- lib/ligarb/writer.rb
|
|
85
86
|
- templates/book.html.erb
|
|
86
87
|
homepage: https://github.com/ligarb/ligarb
|
|
87
88
|
licenses:
|