docco 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8f3ccd592996f2ebbe44ee3b9209210f2fac0772b6f8994483c6ad7d62d3db34
4
+ data.tar.gz: e82c79a83e7e411a8dffce764b2a16d9749be276c38f21ff00261497aaf9b326
5
+ SHA512:
6
+ metadata.gz: f9ca8eadfe55928385cfe667ade869202186e88a0ee3fc019d413bc73cb8fd0bf52cc789eac296554a797cd216bd0224799d9151a9e39c5c1064a37042d9e537
7
+ data.tar.gz: 71081172e9925c33741b4f5be2d5329f5a8fec02ad27569166b982f6a2a87b0e21451cb93921f4995b6c563c48f09d46b35a7cbe1db6e0136530551cda2c1b86
@@ -0,0 +1,46 @@
1
+ name: Build and Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+
7
+ permissions:
8
+ contents: read
9
+ pages: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Check out repository
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: '3.4.6'
24
+ bundler-cache: true
25
+
26
+ # Your script will overwrite docs/index.html, but style.css stays untouched
27
+ - name: Generate docs HTML
28
+ run: bundle exec rake docco:docs
29
+
30
+ # Upload all files in docs (both static + generated)
31
+ - name: Upload artifact
32
+ uses: actions/upload-pages-artifact@v3
33
+ with:
34
+ path: docs/
35
+
36
+ deploy:
37
+ needs: build
38
+ runs-on: ubuntu-latest
39
+ environment:
40
+ name: github-pages
41
+ url: ${{ steps.deployment.outputs.page_url }}
42
+
43
+ steps:
44
+ - name: Deploy to GitHub Pages
45
+ id: deployment
46
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,540 @@
1
+ /* Reset and Base Styles */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ :root {
9
+ --primary-color: #2563eb;
10
+ --primary-dark: #1e40af;
11
+ --secondary-color: #64748b;
12
+ --bg-color: #f8fafc;
13
+ --sidebar-bg: #1e293b;
14
+ --sidebar-text: #cbd5e1;
15
+ --sidebar-hover: #334155;
16
+ --content-bg: #ffffff;
17
+ --text-color: #1e293b;
18
+ --text-light: #64748b;
19
+ --border-color: #e2e8f0;
20
+ --code-bg: #f1f5f9;
21
+ --code-border: #cbd5e1;
22
+ --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
23
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
24
+ }
25
+
26
+ html {
27
+ scroll-behavior: smooth;
28
+ }
29
+
30
+ body {
31
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
32
+ line-height: 1.6;
33
+ color: var(--text-color);
34
+ background-color: var(--bg-color);
35
+ padding-top: 60px;
36
+ }
37
+
38
+ /* Top Menu Bar */
39
+ .top-menu {
40
+ position: fixed;
41
+ top: 0;
42
+ left: 0;
43
+ right: 0;
44
+ height: 60px;
45
+ background-color: var(--content-bg);
46
+ border-bottom: 1px solid var(--border-color);
47
+ box-shadow: var(--shadow);
48
+ z-index: 1000;
49
+ }
50
+
51
+ .top-menu-content {
52
+ max-width: 100%;
53
+ height: 100%;
54
+ padding: 0 2rem;
55
+ display: flex;
56
+ justify-content: space-between;
57
+ align-items: center;
58
+ }
59
+
60
+ .top-menu-brand {
61
+ display: flex;
62
+ align-items: baseline;
63
+ gap: 0.75rem;
64
+ }
65
+
66
+ .brand-name {
67
+ font-size: 1.5rem;
68
+ font-weight: 700;
69
+ color: var(--text-color);
70
+ }
71
+
72
+ .brand-tagline {
73
+ font-size: 0.875rem;
74
+ color: var(--text-light);
75
+ font-weight: 400;
76
+ }
77
+
78
+ .top-menu .github-link {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 0.5rem;
82
+ padding: 0.5rem 1rem;
83
+ background-color: var(--text-color);
84
+ color: #ffffff;
85
+ border-radius: 6px;
86
+ text-decoration: none;
87
+ font-weight: 600;
88
+ font-size: 0.875rem;
89
+ transition: all 0.2s ease;
90
+ }
91
+
92
+ .top-menu .github-link:hover {
93
+ background-color: #000000;
94
+ text-decoration: none;
95
+ }
96
+
97
+ .top-menu .github-link svg {
98
+ width: 20px;
99
+ height: 20px;
100
+ }
101
+
102
+ /* Layout */
103
+ .container {
104
+ display: flex;
105
+ min-height: calc(100vh - 60px);
106
+ }
107
+
108
+ /* Sidebar */
109
+ .sidebar {
110
+ width: 280px;
111
+ background-color: var(--sidebar-bg);
112
+ color: var(--sidebar-text);
113
+ padding: 2rem 0;
114
+ position: fixed;
115
+ top: 60px;
116
+ height: calc(100vh - 60px);
117
+ overflow-y: auto;
118
+ box-shadow: var(--shadow-lg);
119
+ }
120
+
121
+ .sidebar::-webkit-scrollbar {
122
+ width: 8px;
123
+ }
124
+
125
+ .sidebar::-webkit-scrollbar-track {
126
+ background: var(--sidebar-bg);
127
+ }
128
+
129
+ .sidebar::-webkit-scrollbar-thumb {
130
+ background: var(--sidebar-hover);
131
+ border-radius: 4px;
132
+ }
133
+
134
+ .logo {
135
+ padding: 0 1.5rem 1.5rem;
136
+ border-bottom: 1px solid var(--sidebar-hover);
137
+ margin-bottom: 1.5rem;
138
+ }
139
+
140
+ .logo h2 {
141
+ font-size: 1.75rem;
142
+ color: #ffffff;
143
+ margin-bottom: 0.25rem;
144
+ font-weight: 700;
145
+ }
146
+
147
+ .logo .tagline {
148
+ font-size: 0.875rem;
149
+ color: var(--sidebar-text);
150
+ font-weight: 400;
151
+ }
152
+
153
+ .nav-menu {
154
+ list-style: none;
155
+ padding-left: 0;
156
+ }
157
+
158
+ .nav-menu li {
159
+ margin: 0;
160
+ }
161
+
162
+ .nav-menu a {
163
+ display: block;
164
+ padding: 0.625rem 1.5rem;
165
+ color: var(--sidebar-text);
166
+ text-decoration: none;
167
+ transition: all 0.2s ease;
168
+ font-size: 0.9375rem;
169
+ }
170
+
171
+ .nav-menu li:not(.nav-submenu) > a {
172
+ font-weight: 600;
173
+ margin-top: 0.5rem;
174
+ }
175
+
176
+ .nav-submenu a {
177
+ padding-left: 2.5rem;
178
+ font-size: 0.875rem;
179
+ color: var(--sidebar-text);
180
+ opacity: 0.9;
181
+ }
182
+
183
+ .nav-menu a:hover {
184
+ background-color: var(--sidebar-hover);
185
+ color: #ffffff;
186
+ }
187
+
188
+ .nav-menu a.active {
189
+ background-color: var(--primary-color);
190
+ color: #ffffff;
191
+ border-left: 3px solid #ffffff;
192
+ padding-left: calc(1.5rem - 3px);
193
+ }
194
+
195
+ .nav-submenu a.active {
196
+ padding-left: calc(2.5rem - 3px);
197
+ }
198
+
199
+ /* Accordion Menus */
200
+ .nav-accordion {
201
+ position: relative;
202
+ }
203
+
204
+ .accordion-toggle {
205
+ display: flex;
206
+ justify-content: space-between;
207
+ align-items: center;
208
+ cursor: pointer;
209
+ }
210
+
211
+ .accordion-icon {
212
+ font-size: 0.75rem;
213
+ transition: transform 0.2s ease;
214
+ margin-left: 0.5rem;
215
+ }
216
+
217
+ .nav-accordion.expanded .accordion-icon {
218
+ transform: rotate(-180deg);
219
+ }
220
+
221
+ .accordion-content {
222
+ list-style: none;
223
+ padding-left: 0;
224
+ max-height: 0;
225
+ overflow: hidden;
226
+ transition: max-height 0.3s ease;
227
+ }
228
+
229
+ .nav-accordion.expanded .accordion-content {
230
+ max-height: 500px;
231
+ }
232
+
233
+ .nav-h4 a {
234
+ padding-left: 3.5rem;
235
+ font-size: 0.8125rem;
236
+ color: var(--sidebar-text);
237
+ opacity: 0.85;
238
+ }
239
+
240
+ .nav-h4 a.active {
241
+ padding-left: calc(3.5rem - 3px);
242
+ }
243
+
244
+ /* Main Content */
245
+ .content {
246
+ flex: 1;
247
+ margin-left: 280px;
248
+ padding: 3rem;
249
+ max-width: 1200px;
250
+ width: 79vw;
251
+ }
252
+
253
+ /* Page Header */
254
+ .page-header {
255
+ margin-bottom: 3rem;
256
+ padding-bottom: 2rem;
257
+ border-bottom: 2px solid var(--border-color);
258
+ }
259
+
260
+ .page-header h1 {
261
+ font-size: 3rem;
262
+ font-weight: 800;
263
+ color: var(--text-color);
264
+ margin-bottom: 0.5rem;
265
+ letter-spacing: -0.025em;
266
+ }
267
+
268
+ .page-header .subtitle {
269
+ font-size: 1.25rem;
270
+ color: var(--text-light);
271
+ font-weight: 400;
272
+ }
273
+
274
+ /* Sections */
275
+ .section {
276
+ margin-bottom: 4rem;
277
+ }
278
+
279
+ .section h2 {
280
+ font-size: 2rem;
281
+ font-weight: 700;
282
+ color: var(--text-color);
283
+ margin-bottom: 1.5rem;
284
+ padding-bottom: 0.5rem;
285
+ border-bottom: 2px solid var(--primary-color);
286
+ }
287
+
288
+ .subsection {
289
+ margin-bottom: 2.5rem;
290
+ background-color: var(--content-bg);
291
+ padding: 2rem;
292
+ border-radius: 8px;
293
+ box-shadow: var(--shadow);
294
+ }
295
+
296
+ .subsection h3 {
297
+ font-size: 1.5rem;
298
+ font-weight: 600;
299
+ color: var(--text-color);
300
+ margin-bottom: 1rem;
301
+ }
302
+
303
+ .subsection h4 {
304
+ font-size: 1.25rem;
305
+ font-weight: 600;
306
+ color: var(--text-color);
307
+ margin: 1.5rem 0 1rem;
308
+ }
309
+
310
+ /* Typography */
311
+ p {
312
+ margin-bottom: 1rem;
313
+ line-height: 1.75;
314
+ }
315
+
316
+ a {
317
+ color: var(--primary-color);
318
+ text-decoration: none;
319
+ }
320
+
321
+ a:hover {
322
+ color: var(--primary-dark);
323
+ text-decoration: underline;
324
+ }
325
+
326
+ strong {
327
+ font-weight: 600;
328
+ color: var(--text-color);
329
+ }
330
+
331
+ /* Lists */
332
+ ul, ol {
333
+ margin-bottom: 1rem;
334
+ padding-left: 1.5rem;
335
+ }
336
+
337
+ li {
338
+ margin-bottom: 0.5rem;
339
+ }
340
+
341
+ .feature-list {
342
+ list-style: none;
343
+ padding-left: 0;
344
+ }
345
+
346
+ .feature-list li {
347
+ padding-left: 1.5rem;
348
+ position: relative;
349
+ margin-bottom: 0.75rem;
350
+ }
351
+
352
+ .feature-list li::before {
353
+ content: "→";
354
+ position: absolute;
355
+ left: 0;
356
+ color: var(--primary-color);
357
+ font-weight: bold;
358
+ }
359
+
360
+ /* Code Blocks */
361
+ code {
362
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
363
+ font-size: 0.875em;
364
+ background-color: var(--code-bg);
365
+ padding: 0.2em 0.4em;
366
+ border-radius: 3px;
367
+ border: 1px solid var(--code-border);
368
+ }
369
+
370
+ pre {
371
+ margin: 1.5rem 0;
372
+ padding: 0;
373
+ overflow-x: auto;
374
+ border-radius: 6px;
375
+ box-shadow: var(--shadow);
376
+ background-color: #282c34;
377
+ }
378
+
379
+ pre code {
380
+ display: block;
381
+ padding: 1.25rem;
382
+ background-color: transparent;
383
+ color: inherit;
384
+ border: none;
385
+ border-radius: 6px;
386
+ line-height: 1.6;
387
+ overflow-x: auto;
388
+ }
389
+
390
+ pre code::-webkit-scrollbar {
391
+ height: 8px;
392
+ }
393
+
394
+ pre code::-webkit-scrollbar-track {
395
+ background: var(--sidebar-hover);
396
+ border-radius: 4px;
397
+ }
398
+
399
+ pre code::-webkit-scrollbar-thumb {
400
+ background: var(--sidebar-text);
401
+ border-radius: 4px;
402
+ }
403
+
404
+ /* Images */
405
+ .image-container {
406
+ margin: 2rem 0;
407
+ text-align: center;
408
+ }
409
+
410
+ .image-container img {
411
+ max-width: 100%;
412
+ height: auto;
413
+ }
414
+
415
+ .image-container .caption {
416
+ margin-top: 0.75rem;
417
+ font-size: 0.875rem;
418
+ color: var(--text-light);
419
+ font-style: italic;
420
+ }
421
+
422
+ @media (max-width: 1248px) {
423
+ .content p img {
424
+ width: 64vw;
425
+ height: auto;
426
+ }
427
+ }
428
+
429
+ /* Responsive Design */
430
+ @media (max-width: 1024px) {
431
+ .sidebar {
432
+ width: 240px;
433
+ }
434
+
435
+ .content {
436
+ width: 76vw;
437
+ margin-left: 240px;
438
+ padding: 2rem;
439
+ }
440
+ }
441
+
442
+ @media (max-width: 768px) {
443
+ /* Stack sidebar and content vertically on handhelds */
444
+ .container {
445
+ flex-direction: column;
446
+ }
447
+ .top-menu-content {
448
+ padding: 0 1rem;
449
+ }
450
+
451
+ .brand-tagline {
452
+ display: none;
453
+ }
454
+
455
+ .top-menu .github-link span {
456
+ display: none;
457
+ }
458
+
459
+ .top-menu .github-link {
460
+ padding: 0.5rem;
461
+ }
462
+
463
+ .sidebar {
464
+ display:none;
465
+ }
466
+
467
+ .content {
468
+ margin-left: 0;
469
+ width: 100vw;
470
+ padding: 1.5rem;
471
+ }
472
+
473
+ .content p img {
474
+ width: 78vw;
475
+ height: auto;
476
+ }
477
+ .page-header h1 {
478
+ font-size: 2rem;
479
+ }
480
+
481
+ .page-header .subtitle {
482
+ font-size: 1rem;
483
+ }
484
+
485
+ .section h2 {
486
+ font-size: 1.5rem;
487
+ }
488
+
489
+ .subsection {
490
+ padding: 1.5rem;
491
+ }
492
+
493
+ .subsection h3 {
494
+ font-size: 1.25rem;
495
+ }
496
+
497
+ pre code {
498
+ padding: 1rem;
499
+ font-size: 0.8125rem;
500
+ }
501
+ }
502
+
503
+ /* Smooth Scrolling and Anchor Offset */
504
+ section {
505
+ scroll-margin-top: 5rem;
506
+ }
507
+
508
+ article {
509
+ scroll-margin-top: 5rem;
510
+ }
511
+
512
+ /* Print Styles */
513
+ @media print {
514
+ .top-menu {
515
+ display: none;
516
+ }
517
+
518
+ body {
519
+ padding-top: 0;
520
+ }
521
+
522
+ .sidebar {
523
+ display: none;
524
+ }
525
+
526
+ .content {
527
+ margin-left: 0;
528
+ max-width: 100%;
529
+ }
530
+
531
+ .subsection {
532
+ box-shadow: none;
533
+ border: 1px solid var(--border-color);
534
+ }
535
+
536
+ pre code {
537
+ background-color: var(--code-bg);
538
+ color: var(--text-color);
539
+ }
540
+ }
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'docco'
4
+
5
+ namespace :docco do
6
+ desc 'Copy default styles.css to host library docs'
7
+ task :css, [:output_dir] do |t, args|
8
+ output_dir = args[:output_dir] || 'docs'
9
+ Docco::CopyStyles.(output_dir)
10
+ end
11
+
12
+ desc 'Generate a Github Action into .github/workflows'
13
+ task :gh do
14
+ Docco::CopyGHAction.()
15
+ end
16
+
17
+ desc "Build documentation website from README"
18
+ task :docs, [:readme_path, :output_dir, :gemspec] do |t, args|
19
+ readme_path = args[:readme_path] || 'README.md'
20
+ output_dir = args[:output_dir] || 'docs'
21
+ gemspec = args[:gemspec]
22
+
23
+ builder = Docco::DocsBuilder.new(
24
+ readme_path: readme_path,
25
+ output_dir: output_dir,
26
+ gemspec_path: gemspec
27
+ )
28
+
29
+ builder.build
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Find the rake file relative to this file
4
+ rakefile = File.expand_path("tasks.rake", __dir__)
5
+
6
+ # Load it only if Rake is available
7
+ if defined?(Rake)
8
+ load rakefile
9
+ else
10
+ warn "[my_gem] Rake not loaded; skipping task definitions"
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docco
4
+ VERSION = "0.1.0"
5
+ end
data/lib/docco.rb ADDED
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kramdown'
4
+ require 'fileutils'
5
+ require_relative "docco/version"
6
+
7
+ module Docco
8
+ STYLES = 'styles.css'
9
+ GHACTION = 'deploy-docs.yml'
10
+ GHACTION_DIR = File.join('.github', 'workflows')
11
+
12
+ CopyGHAction = proc do
13
+ FileUtils.mkdir_p(GHACTION_DIR)
14
+ source = File.join(__dir__, 'docco', GHACTION)
15
+ destination = File.join(GHACTION_DIR, GHACTION)
16
+ FileUtils.cp(source, destination)
17
+ puts "Github action copied to #{destination}"
18
+ end
19
+
20
+ CopyStyles = proc do |output_dir|
21
+ FileUtils.mkdir_p(output_dir)
22
+ source = File.join(__dir__, 'docco', STYLES)
23
+ destination = File.join(output_dir, STYLES)
24
+ FileUtils.cp(source, destination)
25
+ puts "Docco styles copied to #{destination}"
26
+ end
27
+
28
+ class DocsBuilder
29
+ def initialize(readme_path:, output_dir:, gemspec_path: nil)
30
+ @readme_path = readme_path
31
+ @output_dir = output_dir
32
+ @sections = []
33
+ @gemspec_path = gemspec_path || find_gemspec
34
+ load_gemspec_info
35
+ end
36
+
37
+ def build
38
+ css = File.join(@output_dir, STYLES)
39
+ if !File.exist?(css)
40
+ CopyStyles.(@output_dir)
41
+ end
42
+
43
+ puts "Reading #{@readme_path}..."
44
+ markdown = File.read(@readme_path)
45
+
46
+ puts "Parsing markdown..."
47
+ doc = Kramdown::Document.new(markdown, input: 'GFM', auto_ids: true)
48
+
49
+ puts "Extracting structure..."
50
+ extract_structure(doc.root)
51
+
52
+ puts "Generating HTML..."
53
+ html = generate_html(doc)
54
+
55
+ puts "Writing to #{@output_dir}/index.html..."
56
+ FileUtils.mkdir_p(@output_dir)
57
+ File.write(File.join(@output_dir, 'index.html'), html)
58
+
59
+ puts "Done! Documentation built successfully."
60
+ end
61
+
62
+ private
63
+
64
+ def find_gemspec
65
+ gemspecs = Dir.glob('*.gemspec')
66
+ if gemspecs.empty?
67
+ puts "Warning: No gemspec file found. Using default values."
68
+ nil
69
+ elsif gemspecs.size > 1
70
+ puts "Warning: Multiple gemspec files found. Using #{gemspecs.first}"
71
+ gemspecs.first
72
+ else
73
+ gemspecs.first
74
+ end
75
+ end
76
+
77
+ def load_gemspec_info
78
+ if @gemspec_path && File.exist?(@gemspec_path)
79
+ spec = Gem::Specification.load(@gemspec_path)
80
+ @gem_name = spec.name
81
+ @gem_summary = spec.summary
82
+ @gem_description = spec.description
83
+ # Prefer source_code_uri from metadata, fall back to homepage
84
+ @github_url = spec.metadata['source_code_uri'] || spec.homepage
85
+ else
86
+ # Fallback values if no gemspec found
87
+ @gem_name = 'Documentation'
88
+ @gem_summary = 'Project Documentation'
89
+ @gem_description = 'Project Documentation'
90
+ @github_url = ''
91
+ end
92
+ end
93
+
94
+ def extract_structure(element, level = 0)
95
+ element.children.each do |child|
96
+ if child.type == :header
97
+ # Use Kramdown's auto-generated ID
98
+ id = child.attr['id']
99
+ title = extract_text(child)
100
+
101
+ @sections << {
102
+ level: child.options[:level],
103
+ id: id,
104
+ title: title,
105
+ element: child
106
+ }
107
+ end
108
+
109
+ extract_structure(child, level + 1) if child.children
110
+ end
111
+ end
112
+
113
+ def extract_text(element)
114
+ case element.type
115
+ when :text
116
+ element.value
117
+ when :codespan
118
+ element.value
119
+ when :header, :p, :strong, :em
120
+ element.children.map { |c| extract_text(c) }.join if element.children
121
+ else
122
+ if element.children && !element.children.empty?
123
+ element.children.map { |c| extract_text(c) }.join
124
+ else
125
+ ''
126
+ end
127
+ end
128
+ end
129
+
130
+ def generate_navigation
131
+ nav_items = []
132
+ current_section = nil
133
+
134
+ @sections.each do |section|
135
+ if section[:level] == 2
136
+ current_section = section
137
+ nav_items << %(<li><a href="##{section[:id]}">#{section[:title]}</a></li>)
138
+ elsif section[:level] == 3 && current_section
139
+ nav_items << %(<li class="nav-submenu"><a href="##{section[:id]}">#{section[:title]}</a></li>)
140
+ end
141
+ end
142
+
143
+ nav_items.join("\n ")
144
+ end
145
+
146
+ def generate_html(doc)
147
+ # Convert markdown to HTML
148
+ content_html = doc.to_html
149
+
150
+ # Process the HTML to add proper structure
151
+ content_html = wrap_sections(content_html)
152
+
153
+ <<~HTML
154
+ <!DOCTYPE html>
155
+ <html lang="en">
156
+ <head>
157
+ <meta charset="UTF-8">
158
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
159
+ <title>#{@gem_name} - #{@gem_summary}</title>
160
+ <link rel="stylesheet" href="styles.css">
161
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
162
+ </head>
163
+ <body>
164
+ <nav class="top-menu">
165
+ <div class="top-menu-content">
166
+ <div class="top-menu-brand">
167
+ <span class="brand-name">#{@gem_name}</span>
168
+ <span class="brand-tagline">#{@gem_description}</span>
169
+ </div>
170
+ <a href="#{@github_url}" target="_blank" class="github-link" aria-label="View on GitHub">
171
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
172
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
173
+ </svg>
174
+ <span>GitHub</span>
175
+ </a>
176
+ </div>
177
+ </nav>
178
+ <div class="container">
179
+ <nav class="sidebar">
180
+ <div class="logo">
181
+ <h2>#{@gem_name}</h2>
182
+ <p class="tagline">#{@gem_summary}</p>
183
+ </div>
184
+ <ul class="nav-menu">
185
+ #{generate_navigation}
186
+ </ul>
187
+ </nav>
188
+
189
+ <main class="content">
190
+ <header class="page-header">
191
+ <h1>#{@gem_name}</h1>
192
+ <p class="subtitle">#{@gem_summary}</p>
193
+ </header>
194
+
195
+ #{content_html}
196
+ </main>
197
+ </div>
198
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
199
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/ruby.min.js"></script>
200
+ <script>hljs.highlightAll();</script>
201
+ <script>
202
+ // Active section highlighting
203
+ const observerOptions = {
204
+ root: null,
205
+ rootMargin: '-20% 0px -60% 0px',
206
+ threshold: 0
207
+ };
208
+
209
+ const sections = document.querySelectorAll('section, article[id]');
210
+ const navLinks = document.querySelectorAll('.nav-menu a');
211
+
212
+ // Create a map of href to link elements
213
+ const linkMap = new Map();
214
+ navLinks.forEach(link => {
215
+ const href = link.getAttribute('href');
216
+ if (href && href.startsWith('#')) {
217
+ linkMap.set(href, link);
218
+ }
219
+ });
220
+
221
+ const observer = new IntersectionObserver((entries) => {
222
+ entries.forEach(entry => {
223
+ if (entry.isIntersecting) {
224
+ const id = entry.target.getAttribute('id');
225
+ const activeLink = linkMap.get(`#${id}`);
226
+
227
+ if (activeLink) {
228
+ // Remove active class from all links
229
+ navLinks.forEach(link => link.classList.remove('active'));
230
+ // Add active class to current link
231
+ activeLink.classList.add('active');
232
+
233
+ // Update URL hash without scrolling
234
+ if (history.replaceState) {
235
+ history.replaceState(null, null, `#${id}`);
236
+ } else {
237
+ window.location.hash = id;
238
+ }
239
+ }
240
+ }
241
+ });
242
+ }, observerOptions);
243
+
244
+ // Observe all sections
245
+ sections.forEach(section => {
246
+ if (section.id) {
247
+ observer.observe(section);
248
+ }
249
+ });
250
+ </script>
251
+ </body>
252
+ </html>
253
+ HTML
254
+ end
255
+
256
+ def wrap_sections(html)
257
+ # Remove the first h1 (title) as it's in the page header
258
+ html = html.sub(/<h1[^>]*>.*?<\/h1>/, '')
259
+
260
+ # Wrap h2 sections
261
+ html.gsub!(/<h2 id="([^"]+)">(.+?)<\/h2>/) do
262
+ id = $1
263
+ title = $2
264
+ if id == 'usage'
265
+ %(</section>\n\n<section id="#{id}" class="section">\n<h2 id="#{id}">#{title}</h2>)
266
+ else
267
+ %(<section id="#{id}" class="section">\n<h2 id="#{id}">#{title}</h2>)
268
+ end
269
+ end
270
+
271
+ # Wrap h3 subsections
272
+ html.gsub!(/<h3 id="([^"]+)">(.+?)<\/h3>/) do
273
+ id = $1
274
+ title = $2
275
+ %(</article>\n\n<article id="#{id}" class="subsection">\n<h3 id="#{id}">#{title}</h3>)
276
+ end
277
+
278
+ # Close any remaining open sections
279
+ html += "\n</article>\n</section>" if html.include?('<section') || html.include?('<article')
280
+
281
+ # Wrap first section (Overview)
282
+ html = "<section id=\"overview\" class=\"section\">\n" + html
283
+
284
+ # Clean up multiple closing tags
285
+ html.gsub!(/(<\/article>\s*){2,}/, '</article>')
286
+ html.gsub!(/(<\/section>\s*){2,}/, '</section>')
287
+
288
+ html
289
+ end
290
+ end
291
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docco
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ismael Celis
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: kramdown
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: kramdown-parser-gfm
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: Builds static HTML documentation from a Ruby gem's README
41
+ email:
42
+ - ismaelct@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/docco.rb
48
+ - lib/docco/deploy-docs.yml
49
+ - lib/docco/styles.css
50
+ - lib/docco/tasks.rake
51
+ - lib/docco/tasks.rb
52
+ - lib/docco/version.rb
53
+ homepage: https://github.com/ismasan/docco
54
+ licenses: []
55
+ metadata:
56
+ homepage_uri: https://github.com/ismasan/docco
57
+ post_install_message: |2+
58
+
59
+ +----------------------------+
60
+ Docco is now installed.
61
+ Add the following in your library's Rakefile:
62
+
63
+ require 'docco/tasks'
64
+
65
+ Now you can run `bundle exec rake docco:docs` to generate HTML docs from your README.md and .gemspec
66
+
67
+ You can also run `bundle exec rake docco:gh` to add a Github action to generate docs to Github Pages on deply.
68
+ +-----------------------------+
69
+
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 3.2.0
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.6.9
85
+ specification_version: 4
86
+ summary: Builds static HTML documentation from a Ruby gem's README
87
+ test_files: []
88
+ ...