ligarb 0.4.0 → 0.6.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.
@@ -1,9 +1,9 @@
1
1
  <!DOCTYPE html>
2
- <html lang="<%= language %>">
2
+ <html lang="<%= h(language) %>">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title><%= title %></title>
6
+ <title><%= h(title) %></title>
7
7
  <%- if ai_generated -%>
8
8
  <meta name="robots" content="noindex, nofollow, noarchive">
9
9
  <meta name="robots" content="noai, noimageai">
@@ -30,14 +30,14 @@
30
30
  <div class="sidebar-header-top">
31
31
  <%- cover_chapter = chapters.find(&:cover?) -%>
32
32
  <%- if cover_chapter -%>
33
- <h1 class="book-title"><a href="#<%= cover_chapter.slug %>" onclick="showChapter('<%= cover_chapter.slug %>'); return false;"><%= title %></a></h1>
33
+ <h1 class="book-title"><a href="#<%= cover_chapter.slug %>" onclick="showChapter('<%= cover_chapter.slug %>'); return false;"><%= h(title) %></a></h1>
34
34
  <%- else -%>
35
- <h1 class="book-title"><%= title %></h1>
35
+ <h1 class="book-title"><%= h(title) %></h1>
36
36
  <%- end -%>
37
37
  <button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode" title="Toggle dark mode">&#9790;</button>
38
38
  </div>
39
39
  <%- unless author.empty? -%>
40
- <p class="book-author"><%= author %></p>
40
+ <p class="book-author"><%= h(author) %></p>
41
41
  <%- end -%>
42
42
  <%- if ai_generated -%>
43
43
  <span class="ai-badge"><%= language == 'ja' ? 'AI 生成' : 'AI Generated' %></span>
@@ -54,13 +54,13 @@
54
54
  <%- when :cover -%>
55
55
  <%- when :chapter -%>
56
56
  <li class="toc-chapter" data-chapter="<%= node.chapter.slug %>">
57
- <a href="#<%= node.chapter.slug %>" class="toc-h1" onclick="showChapter('<%= node.chapter.slug %>')"><%= node.chapter.display_title %></a>
57
+ <a href="#<%= node.chapter.slug %>" class="toc-h1" onclick="showChapter('<%= node.chapter.slug %>')"><%= h(node.chapter.display_title) %></a>
58
58
  <%- sub_headings = node.chapter.headings.select { |h| h.level >= 2 } -%>
59
59
  <%- unless sub_headings.empty? -%>
60
60
  <ul>
61
61
  <%- sub_headings.each do |heading| -%>
62
62
  <li>
63
- <a href="#<%= node.chapter.slug %>--<%= heading.id %>" class="toc-h<%= heading.level %>" onclick="showChapterAndScroll('<%= node.chapter.slug %>', '<%= node.chapter.slug %>--<%= heading.id %>')"><%= heading.display_text %></a>
63
+ <a href="#<%= node.chapter.slug %>--<%= heading.id %>" class="toc-h<%= heading.level %>" onclick="showChapterAndScroll('<%= node.chapter.slug %>', '<%= node.chapter.slug %>--<%= heading.id %>')"><%= h(heading.display_text) %></a>
64
64
  </li>
65
65
  <%- end -%>
66
66
  </ul>
@@ -68,17 +68,17 @@
68
68
  </li>
69
69
  <%- when :part -%>
70
70
  <li class="toc-part">
71
- <a href="#<%= node.chapter.slug %>" class="toc-part-title" onclick="showChapter('<%= node.chapter.slug %>')"><%= node.chapter.title %></a>
71
+ <a href="#<%= node.chapter.slug %>" class="toc-part-title" onclick="showChapter('<%= node.chapter.slug %>')"><%= h(node.chapter.title) %></a>
72
72
  <ul>
73
73
  <%- (node.children || []).each do |child| -%>
74
74
  <li class="toc-chapter" data-chapter="<%= child.chapter.slug %>">
75
- <a href="#<%= child.chapter.slug %>" class="toc-h1" onclick="showChapter('<%= child.chapter.slug %>')"><%= child.chapter.display_title %></a>
75
+ <a href="#<%= child.chapter.slug %>" class="toc-h1" onclick="showChapter('<%= child.chapter.slug %>')"><%= h(child.chapter.display_title) %></a>
76
76
  <%- sub_headings = child.chapter.headings.select { |h| h.level >= 2 } -%>
77
77
  <%- unless sub_headings.empty? -%>
78
78
  <ul>
79
79
  <%- sub_headings.each do |heading| -%>
80
80
  <li>
81
- <a href="#<%= child.chapter.slug %>--<%= heading.id %>" class="toc-h<%= heading.level %>" onclick="showChapterAndScroll('<%= child.chapter.slug %>', '<%= child.chapter.slug %>--<%= heading.id %>')"><%= heading.display_text %></a>
81
+ <a href="#<%= child.chapter.slug %>--<%= heading.id %>" class="toc-h<%= heading.level %>" onclick="showChapterAndScroll('<%= child.chapter.slug %>', '<%= child.chapter.slug %>--<%= heading.id %>')"><%= h(heading.display_text) %></a>
82
82
  </li>
83
83
  <%- end -%>
84
84
  </ul>
@@ -89,17 +89,17 @@
89
89
  </li>
90
90
  <%- when :appendix_group -%>
91
91
  <li class="toc-appendix">
92
- <span class="toc-appendix-title"><%= appendix_label %></span>
92
+ <span class="toc-appendix-title"><%= h(appendix_label) %></span>
93
93
  <ul>
94
94
  <%- (node.children || []).each do |child| -%>
95
95
  <li class="toc-chapter" data-chapter="<%= child.chapter.slug %>">
96
- <a href="#<%= child.chapter.slug %>" class="toc-h1" onclick="showChapter('<%= child.chapter.slug %>')"><%= child.chapter.display_title %></a>
96
+ <a href="#<%= child.chapter.slug %>" class="toc-h1" onclick="showChapter('<%= child.chapter.slug %>')"><%= h(child.chapter.display_title) %></a>
97
97
  <%- sub_headings = child.chapter.headings.select { |h| h.level >= 2 } -%>
98
98
  <%- unless sub_headings.empty? -%>
99
99
  <ul>
100
100
  <%- sub_headings.each do |heading| -%>
101
101
  <li>
102
- <a href="#<%= child.chapter.slug %>--<%= heading.id %>" class="toc-h<%= heading.level %>" onclick="showChapterAndScroll('<%= child.chapter.slug %>', '<%= child.chapter.slug %>--<%= heading.id %>')"><%= heading.display_text %></a>
102
+ <a href="#<%= child.chapter.slug %>--<%= heading.id %>" class="toc-h<%= heading.level %>" onclick="showChapterAndScroll('<%= child.chapter.slug %>', '<%= child.chapter.slug %>--<%= heading.id %>')"><%= h(heading.display_text) %></a>
103
103
  </li>
104
104
  <%- end -%>
105
105
  </ul>
@@ -110,6 +110,11 @@
110
110
  </li>
111
111
  <%- end -%>
112
112
  <%- end -%>
113
+ <%- unless bibliography.empty? -%>
114
+ <li class="toc-chapter" data-chapter="__bibliography__">
115
+ <a href="#__bibliography__" class="toc-h1" onclick="showChapter('__bibliography__')"><%= language == 'ja' ? '参考文献' : 'Bibliography' %></a>
116
+ </li>
117
+ <%- end -%>
113
118
  <%- unless index_tree.empty? -%>
114
119
  <li class="toc-chapter" data-chapter="__index__">
115
120
  <a href="#__index__" class="toc-h1" onclick="showChapter('__index__')"><%= language == 'ja' ? '索引' : 'Index' %></a>
@@ -127,26 +132,36 @@
127
132
  <%= chapter.html %>
128
133
  <%- if repository && !chapter.part_title? && !chapter.cover? -%>
129
134
  <div class="edit-link">
130
- <a href="<%= repository.chomp('/') %>/blob/HEAD/<%= chapter.relative_path %>" target="_blank" rel="noopener">View on GitHub</a>
135
+ <a href="<%= h(repository.chomp('/')) %>/blob/HEAD/<%= h(chapter.relative_path) %>" target="_blank" rel="noopener">View on GitHub</a>
131
136
  </div>
132
137
  <%- end -%>
133
138
  <%- if footer && !chapter.part_title? && !chapter.cover? -%>
134
- <div class="chapter-footer"><%= footer %></div>
139
+ <div class="chapter-footer"><%= h(footer) %></div>
135
140
  <%- end -%>
136
141
  <%- unless chapter.cover? -%>
137
142
  <nav class="chapter-nav">
138
143
  <%- if idx > 0 -%>
139
- <a href="#" class="nav-prev" onclick="showChapter('<%= chapters[idx-1].slug %>'); return false;">&larr; <%= chapters[idx-1].display_title %></a>
144
+ <a href="#" class="nav-prev" onclick="showChapter('<%= chapters[idx-1].slug %>'); return false;">&larr; <%= h(chapters[idx-1].display_title) %></a>
140
145
  <%- else -%>
141
146
  <span></span>
142
147
  <%- end -%>
143
148
  <%- if idx < chapters.size - 1 -%>
144
- <a href="#" class="nav-next" onclick="showChapter('<%= chapters[idx+1].slug %>'); return false;"><%= chapters[idx+1].display_title %> &rarr;</a>
149
+ <a href="#" class="nav-next" onclick="showChapter('<%= chapters[idx+1].slug %>'); return false;"><%= h(chapters[idx+1].display_title) %> &rarr;</a>
145
150
  <%- end -%>
146
151
  </nav>
147
152
  <%- end -%>
148
153
  </section>
149
154
  <%- end -%>
155
+ <%- unless bibliography.empty? -%>
156
+ <section class="chapter bibliography-chapter" id="chapter-__bibliography__" style="display: none;">
157
+ <h1><%= language == 'ja' ? '参考文献' : 'Bibliography' %></h1>
158
+ <ul class="bibliography-list">
159
+ <%- bibliography.each do |entry| -%>
160
+ <li id="bib-<%= h(entry[:key]) %>"><span class="bib-label">[<%= h(entry[:label]) %>]</span> <%= entry[:formatted_html] %></li>
161
+ <%- end -%>
162
+ </ul>
163
+ </section>
164
+ <%- end -%>
150
165
  <%- unless index_tree.empty? -%>
151
166
  <section class="chapter index-chapter" id="chapter-__index__" style="display: none;">
152
167
  <h1><%= language == 'ja' ? '索引' : 'Index' %></h1>
@@ -155,12 +170,12 @@
155
170
  <h2 class="index-letter"><%= letter %></h2>
156
171
  <dl class="index-entries">
157
172
  <%- index_tree[letter].each do |item| -%>
158
- <dt><%= item[:term] %></dt>
173
+ <dt><%= h(item[:term]) %></dt>
159
174
  <%- item[:refs].each do |ref| -%>
160
175
  <dd><a href="#<%= ref[:anchor_id] %>" onclick="showChapterAndScroll('<%= ref[:chapter_slug] %>', '<%= ref[:anchor_id] %>'); return false;"><%= ref[:chapter_title] %></a></dd>
161
176
  <%- end -%>
162
177
  <%- item[:children].each do |child| -%>
163
- <dt class="index-sub"><%= child[:term] %></dt>
178
+ <dt class="index-sub"><%= h(child[:term]) %></dt>
164
179
  <%- child[:refs].each do |ref| -%>
165
180
  <dd class="index-sub"><a href="#<%= ref[:anchor_id] %>" onclick="showChapterAndScroll('<%= ref[:chapter_slug] %>', '<%= ref[:anchor_id] %>'); return false;"><%= ref[:chapter_title] %></a></dd>
166
181
  <%- end -%>
@@ -175,10 +190,12 @@
175
190
 
176
191
  <script>
177
192
  (function() {
178
- var chapters = [<%= chapters.map { |c| "\"#{c.slug}\"" }.join(", ") %><%= ', "__index__"' unless index_tree.empty? %>];
193
+ var chapters = [<%= chapters.map { |c| "\"#{c.slug}\"" }.join(", ") %><%= ', "__bibliography__"' unless bibliography.empty? %><%= ', "__index__"' unless index_tree.empty? %>];
179
194
  var currentChapter = null;
180
195
 
181
- function showChapter(slug) {
196
+ var navigating = false; // flag to suppress pushState during popstate/initial load
197
+
198
+ function showChapter(slug, hash) {
182
199
  chapters.forEach(function(ch) {
183
200
  var el = document.getElementById('chapter-' + ch);
184
201
  if (el) el.style.display = (ch === slug) ? 'block' : 'none';
@@ -195,7 +212,10 @@
195
212
  });
196
213
 
197
214
  currentChapter = slug;
198
- history.replaceState(null, '', '#' + slug);
215
+ var newHash = '#' + (hash || slug);
216
+ if (!navigating) {
217
+ history.pushState(null, '', newHash);
218
+ }
199
219
  window.scrollTo(0, 0);
200
220
 
201
221
  // Re-apply highlight if search is active
@@ -228,39 +248,133 @@
228
248
  }
229
249
  });
230
250
  }
251
+ if (typeof functionPlot !== 'undefined') {
252
+ section.querySelectorAll('.functionplot[data-plot]').forEach(function(el) {
253
+ if (el.childNodes.length > 0) return;
254
+ try {
255
+ var spec = el.getAttribute('data-plot');
256
+ var opts = parseFunctionPlotSpec(spec);
257
+ opts.target = el;
258
+ functionPlot(opts);
259
+ } catch(e) {
260
+ el.textContent = 'Error rendering plot: ' + e.message;
261
+ el.style.color = 'red';
262
+ }
263
+ });
264
+ }
265
+ }
266
+
267
+ // Parse functionplot spec: lines of "y = expr" and "key: value" options
268
+ function parseFunctionPlotSpec(spec) {
269
+ var lines = spec.split('\n');
270
+ var fns = [];
271
+ var opts = {width: 600, height: 400};
272
+ lines.forEach(function(line) {
273
+ line = line.trim();
274
+ if (!line) return;
275
+ // Option lines: key: value
276
+ var optMatch = line.match(/^(xrange|yrange|range|width|height|title|grid)\s*:\s*(.+)$/i);
277
+ if (optMatch) {
278
+ var key = optMatch[1].toLowerCase();
279
+ var val = optMatch[2].trim();
280
+ if (key === 'range' || key === 'xrange') {
281
+ opts.xAxis = {domain: parseRange(val)};
282
+ } else if (key === 'yrange') {
283
+ opts.yAxis = {domain: parseRange(val)};
284
+ } else if (key === 'width') {
285
+ opts.width = parseInt(val, 10);
286
+ } else if (key === 'height') {
287
+ opts.height = parseInt(val, 10);
288
+ } else if (key === 'title') {
289
+ opts.title = val;
290
+ } else if (key === 'grid') {
291
+ opts.grid = val === 'true';
292
+ }
293
+ return;
294
+ }
295
+ // Function lines: y = expr, r = expr (polar), x = expr (implicit), or bare expr
296
+ var fnMatch = line.match(/^y\s*=\s*(.+)$/);
297
+ if (fnMatch) { fns.push({fn: fnMatch[1].trim()}); return; }
298
+ var polarMatch = line.match(/^r\s*=\s*(.+)$/);
299
+ if (polarMatch) { fns.push({r: polarMatch[1].trim(), fnType: 'polar', graphType: 'polyline'}); return; }
300
+ var paramMatch = line.match(/^parametric\s*:\s*(.+?)\s*,\s*(.+)$/);
301
+ if (paramMatch) { fns.push({x: paramMatch[1].trim(), y: paramMatch[2].trim(), fnType: 'parametric', graphType: 'polyline'}); return; }
302
+ // Bare expression treated as y = expr
303
+ fns.push({fn: line});
304
+ });
305
+ opts.data = fns;
306
+ return opts;
307
+ }
308
+
309
+ function parseRange(str) {
310
+ var m = str.match(/\[?\s*(-?[\d.eE+piPI*\/]+)\s*,\s*(-?[\d.eE+piPI*\/]+)\s*\]?/);
311
+ if (!m) return [-10, 10];
312
+ return [evalRangeNum(m[1]), evalRangeNum(m[2])];
313
+ }
314
+
315
+ function evalRangeNum(s) {
316
+ s = s.replace(/pi/gi, String(Math.PI));
317
+ try { return Function('"use strict"; return (' + s + ')')(); }
318
+ catch(e) { return parseFloat(s) || 0; }
231
319
  }
232
320
 
233
321
  function showChapterAndScroll(slug, headingId) {
234
- showChapter(slug);
235
- history.replaceState(null, '', '#' + headingId);
322
+ showChapter(slug, headingId);
236
323
  setTimeout(function() {
237
324
  var target = document.getElementById(headingId);
238
- if (target) target.scrollIntoView({ behavior: 'smooth' });
325
+ if (target) {
326
+ target.scrollIntoView({ behavior: 'smooth' });
327
+ highlightElement(target);
328
+ }
239
329
  }, 50);
240
330
  }
241
331
 
242
- // Handle initial hash
332
+ function highlightElement(el) {
333
+ var prev = document.querySelector('.ligarb-highlight');
334
+ if (prev) prev.classList.remove('ligarb-highlight');
335
+ el.classList.add('ligarb-highlight');
336
+ }
337
+
338
+ // Navigate to chapter/heading based on current URL hash
243
339
  function handleHash() {
340
+ navigating = true;
244
341
  var hash = location.hash.replace('#', '');
245
342
  if (!hash) {
246
343
  if (chapters.length > 0) showChapter(chapters[0]);
344
+ navigating = false;
247
345
  return;
248
346
  }
249
347
 
250
348
  // Check if hash matches a chapter slug directly
251
349
  if (chapters.indexOf(hash) !== -1) {
252
350
  showChapter(hash);
351
+ navigating = false;
253
352
  return;
254
353
  }
255
354
 
355
+ <%- unless bibliography.empty? -%>
356
+ // Check for bibliography entry (bib-KEY)
357
+ var bibMatch = hash.match(/^bib-(.+)/);
358
+ if (bibMatch) {
359
+ showChapter('__bibliography__', hash);
360
+ setTimeout(function() {
361
+ var target = document.getElementById(hash);
362
+ if (target) target.scrollIntoView({ behavior: 'smooth' });
363
+ }, 50);
364
+ navigating = false;
365
+ return;
366
+ }
367
+ <%- end -%>
368
+
256
369
  // Check for footnote links (fn: or fnref:)
257
370
  var fnMatch = hash.match(/^(?:fn|fnref):(.+?)--/);
258
371
  if (fnMatch) {
259
372
  var fnChSlug = fnMatch[1];
260
373
  if (chapters.indexOf(fnChSlug) !== -1) {
261
- if (currentChapter !== fnChSlug) showChapter(fnChSlug);
374
+ if (currentChapter !== fnChSlug) showChapter(fnChSlug, hash);
262
375
  var fnTarget = document.getElementById(hash);
263
376
  if (fnTarget) fnTarget.scrollIntoView({ behavior: 'smooth' });
377
+ navigating = false;
264
378
  return;
265
379
  }
266
380
  }
@@ -271,26 +385,98 @@
271
385
  var chSlug = parts[0];
272
386
  if (chapters.indexOf(chSlug) !== -1) {
273
387
  showChapterAndScroll(chSlug, hash);
388
+ navigating = false;
274
389
  return;
275
390
  }
276
391
  }
277
392
 
278
393
  // Fallback
279
394
  if (chapters.length > 0) showChapter(chapters[0]);
395
+ navigating = false;
280
396
  }
281
397
 
282
398
  // TOC search + content highlight
283
399
  var searchInput = document.getElementById('toc-search');
284
400
  var clearBtn = document.getElementById('search-clear');
285
401
 
402
+ // Build text cache for full-text search (lazy, built on first search)
403
+ var chapterTextCache = null;
404
+ function buildTextCache() {
405
+ if (chapterTextCache) return;
406
+ chapterTextCache = {};
407
+ chapters.forEach(function(slug) {
408
+ var section = document.getElementById('chapter-' + slug);
409
+ if (section) {
410
+ chapterTextCache[slug] = section.textContent.toLowerCase();
411
+ }
412
+ });
413
+ }
414
+
415
+ function countMatches(text, query) {
416
+ var count = 0;
417
+ var idx = text.indexOf(query);
418
+ while (idx !== -1) {
419
+ count++;
420
+ idx = text.indexOf(query, idx + query.length);
421
+ }
422
+ return count;
423
+ }
424
+
286
425
  function updateSearch() {
287
426
  var query = searchInput.value.toLowerCase();
427
+ clearBtn.style.display = query ? 'block' : 'none';
428
+
429
+ // Remove existing match count badges
430
+ document.querySelectorAll('.search-match-count').forEach(function(el) { el.remove(); });
431
+
288
432
  var items = document.querySelectorAll('.toc-chapter');
433
+ var partItems = document.querySelectorAll('.toc-part, .toc-appendix');
434
+
435
+ if (!query || query.length < 2) {
436
+ // Show all items when query is too short
437
+ items.forEach(function(item) { item.style.display = ''; });
438
+ partItems.forEach(function(item) { item.style.display = ''; });
439
+ highlightContent('');
440
+ return;
441
+ }
442
+
443
+ buildTextCache();
444
+
445
+ // Track which chapters match (content or TOC text)
446
+ var matchingSlugs = {};
289
447
  items.forEach(function(item) {
290
- var text = item.textContent.toLowerCase();
291
- item.style.display = text.indexOf(query) !== -1 ? '' : 'none';
448
+ var slug = item.dataset.chapter;
449
+ var tocText = item.textContent.toLowerCase();
450
+ var contentText = chapterTextCache[slug] || '';
451
+ var tocMatch = tocText.indexOf(query) !== -1;
452
+ var contentMatch = contentText.indexOf(query) !== -1;
453
+
454
+ if (tocMatch || contentMatch) {
455
+ item.style.display = '';
456
+ matchingSlugs[slug] = true;
457
+ // Show match count badge for content matches
458
+ if (contentMatch) {
459
+ var count = countMatches(contentText, query);
460
+ var badge = document.createElement('span');
461
+ badge.className = 'search-match-count';
462
+ badge.textContent = count;
463
+ var link = item.querySelector('a');
464
+ if (link) link.appendChild(badge);
465
+ }
466
+ } else {
467
+ item.style.display = 'none';
468
+ }
292
469
  });
293
- clearBtn.style.display = query ? 'block' : 'none';
470
+
471
+ // Show/hide part and appendix headers based on whether they have visible children
472
+ partItems.forEach(function(item) {
473
+ var children = item.querySelectorAll('.toc-chapter');
474
+ var hasVisible = Array.prototype.some.call(children, function(ch) {
475
+ return ch.style.display !== 'none';
476
+ });
477
+ item.style.display = hasVisible ? '' : 'none';
478
+ });
479
+
294
480
  highlightContent(searchInput.value);
295
481
  }
296
482
 
@@ -410,9 +596,12 @@
410
596
  window.showChapterAndScroll = showChapterAndScroll;
411
597
  window.renderSpecialBlocks = function() { if (currentChapter) renderSpecialBlocks(currentChapter); };
412
598
 
413
- // Initialize
599
+ // Initialize (replace initial entry so first back goes to previous page, not same page)
600
+ navigating = true;
414
601
  handleHash();
415
- window.addEventListener('hashchange', handleHash);
602
+ history.replaceState(null, '', location.hash || '#' + (chapters[0] || ''));
603
+ navigating = false;
604
+ window.addEventListener('popstate', handleHash);
416
605
  })();
417
606
  </script>
418
607
  <%- if assets.need?(:highlight) -%>
@@ -430,5 +619,10 @@ renderSpecialBlocks();
430
619
  <script src="js/katex.min.js"></script>
431
620
  <script>renderSpecialBlocks();</script>
432
621
  <%- end -%>
622
+ <%- if assets.need?(:functionplot) -%>
623
+ <script src="js/d3.min.js"></script>
624
+ <script src="js/function-plot.min.js"></script>
625
+ <script>renderSpecialBlocks();</script>
626
+ <%- end -%>
433
627
  </body>
434
628
  </html>
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.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ligarb contributors
@@ -37,6 +37,34 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: webrick
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.7'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ - !ruby/object:Gem::Dependency
55
+ name: fiddle
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '1.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '1.1'
40
68
  - !ruby/object:Gem::Dependency
41
69
  name: rake
42
70
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +100,21 @@ executables:
72
100
  extensions: []
73
101
  extra_rdoc_files: []
74
102
  files:
103
+ - assets/review.css
104
+ - assets/review.js
105
+ - assets/serve.js
75
106
  - assets/style.css
76
107
  - exe/ligarb
77
108
  - lib/ligarb/asset_manager.rb
78
109
  - lib/ligarb/builder.rb
79
110
  - lib/ligarb/chapter.rb
111
+ - lib/ligarb/claude_runner.rb
80
112
  - lib/ligarb/cli.rb
81
113
  - lib/ligarb/config.rb
82
114
  - lib/ligarb/initializer.rb
115
+ - lib/ligarb/inotify.rb
116
+ - lib/ligarb/review_store.rb
117
+ - lib/ligarb/server.rb
83
118
  - lib/ligarb/template.rb
84
119
  - lib/ligarb/version.rb
85
120
  - lib/ligarb/writer.rb