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 +7 -0
- data/lib/docco/deploy-docs.yml +46 -0
- data/lib/docco/styles.css +540 -0
- data/lib/docco/tasks.rake +31 -0
- data/lib/docco/tasks.rb +11 -0
- data/lib/docco/version.rb +5 -0
- data/lib/docco.rb +291 -0
- metadata +88 -0
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
|
data/lib/docco/tasks.rb
ADDED
|
@@ -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
|
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
|
+
...
|