docyard 0.4.0 → 0.5.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/CHANGELOG.md +10 -3
- data/README.md +37 -12
- data/lib/docyard/components/heading_anchor_processor.rb +34 -0
- data/lib/docyard/components/table_of_contents_processor.rb +64 -0
- data/lib/docyard/config.rb +11 -0
- data/lib/docyard/icons/phosphor.rb +2 -1
- data/lib/docyard/markdown.rb +6 -0
- data/lib/docyard/prev_next_builder.rb +159 -0
- data/lib/docyard/rack_application.rb +24 -2
- data/lib/docyard/renderer.rb +16 -5
- data/lib/docyard/templates/assets/css/components/heading-anchor.css +77 -0
- data/lib/docyard/templates/assets/css/components/prev-next.css +114 -0
- data/lib/docyard/templates/assets/css/components/table-of-contents.css +269 -0
- data/lib/docyard/templates/assets/css/layout.css +58 -1
- data/lib/docyard/templates/assets/css/variables.css +1 -0
- data/lib/docyard/templates/assets/js/components/heading-anchor.js +90 -0
- data/lib/docyard/templates/assets/js/components/navigation.js +6 -2
- data/lib/docyard/templates/assets/js/components/table-of-contents.js +301 -0
- data/lib/docyard/templates/layouts/default.html.erb +9 -1
- data/lib/docyard/templates/partials/_heading_anchor.html.erb +1 -0
- data/lib/docyard/templates/partials/_prev_next.html.erb +23 -0
- data/lib/docyard/templates/partials/_table_of_contents.html.erb +45 -0
- data/lib/docyard/templates/partials/_table_of_contents_toggle.html.erb +8 -0
- data/lib/docyard/version.rb +1 -1
- metadata +13 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* Previous/Next Navigation Footer */
|
|
2
|
+
|
|
3
|
+
.doc-footer {
|
|
4
|
+
margin-top: 48px;
|
|
5
|
+
padding-top: 24px;
|
|
6
|
+
border-top: 1px solid var(--color-border);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.pager {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: repeat(2, 1fr);
|
|
12
|
+
gap: 8px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.pager-link {
|
|
16
|
+
display: block;
|
|
17
|
+
padding: 16px;
|
|
18
|
+
border: 1px solid var(--color-border);
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
text-decoration: none !important;
|
|
21
|
+
transition: border-color 0.25s ease, background-color 0.25s ease;
|
|
22
|
+
outline: none;
|
|
23
|
+
background-color: transparent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.pager-link:hover {
|
|
27
|
+
border-color: var(--color-primary);
|
|
28
|
+
background-color: var(--color-background-soft);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.pager-link:focus-visible {
|
|
32
|
+
outline: 2px solid var(--color-primary);
|
|
33
|
+
outline-offset: 2px;
|
|
34
|
+
border-radius: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.pager-link.prev {
|
|
38
|
+
text-align: left;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.pager-link.next {
|
|
42
|
+
text-align: right;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.pager-label {
|
|
46
|
+
display: block;
|
|
47
|
+
font-size: 13px;
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
color: var(--color-text-secondary);
|
|
50
|
+
margin-bottom: 0px;
|
|
51
|
+
text-decoration: none !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.pager-title {
|
|
55
|
+
display: block;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
color: var(--color-text-primary);
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
text-overflow: ellipsis;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
text-decoration: none !important;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Mobile responsive */
|
|
66
|
+
@media (max-width: 640px) {
|
|
67
|
+
.doc-footer {
|
|
68
|
+
margin-top: 48px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.pager {
|
|
72
|
+
grid-template-columns: 1fr;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.pager-link.next {
|
|
76
|
+
margin-left: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.pager-link {
|
|
80
|
+
width: 100%;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.pager-title {
|
|
84
|
+
white-space: normal;
|
|
85
|
+
word-break: break-word;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Reduced motion accessibility */
|
|
90
|
+
@media (prefers-reduced-motion: reduce) {
|
|
91
|
+
.pager-link {
|
|
92
|
+
transition: none;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Print: hide navigation */
|
|
97
|
+
@media print {
|
|
98
|
+
.doc-footer {
|
|
99
|
+
display: none;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Visually hidden utility (for screen readers) */
|
|
104
|
+
.visually-hidden {
|
|
105
|
+
position: absolute;
|
|
106
|
+
width: 1px;
|
|
107
|
+
height: 1px;
|
|
108
|
+
padding: 0;
|
|
109
|
+
margin: -1px;
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
clip: rect(0, 0, 0, 0);
|
|
112
|
+
white-space: nowrap;
|
|
113
|
+
border-width: 0;
|
|
114
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/* Table of Contents - Desktop & Mobile */
|
|
2
|
+
|
|
3
|
+
.docyard-toc {
|
|
4
|
+
width: var(--toc-width);
|
|
5
|
+
position: fixed;
|
|
6
|
+
top: var(--header-height);
|
|
7
|
+
right: 0;
|
|
8
|
+
height: calc(100vh - var(--header-height));
|
|
9
|
+
padding: var(--space-3) var(--space-6) var(--space-3) var(--space-8);
|
|
10
|
+
overflow-y: auto;
|
|
11
|
+
overflow-x: hidden;
|
|
12
|
+
border-left: 1px solid var(--color-border);
|
|
13
|
+
background-color: var(--color-bg);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* TOC Header */
|
|
17
|
+
.docyard-toc__header {
|
|
18
|
+
margin-bottom: var(--space-4);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.docyard-toc__title {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 0.375rem;
|
|
25
|
+
font-size: 0.6875rem;
|
|
26
|
+
font-weight: var(--font-weight-semibold);
|
|
27
|
+
color: var(--color-text-tertiary);
|
|
28
|
+
text-transform: uppercase;
|
|
29
|
+
letter-spacing: 0.1em;
|
|
30
|
+
margin-bottom: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.docyard-toc__title svg {
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Secondary Header TOC Toggle (tablet & mobile) */
|
|
38
|
+
.secondary-header-toc-toggle {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Tablet & Mobile TOC Toggle */
|
|
43
|
+
@media (max-width: 1280px) {
|
|
44
|
+
.secondary-header-toc-toggle {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: var(--space-2);
|
|
48
|
+
padding: var(--space-2) var(--space-3);
|
|
49
|
+
background: transparent;
|
|
50
|
+
border: none;
|
|
51
|
+
color: var(--color-text);
|
|
52
|
+
font-size: var(--font-size-sm);
|
|
53
|
+
font-weight: var(--font-weight-medium);
|
|
54
|
+
font-family: var(--font-sans);
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
transition: color var(--transition-fast);
|
|
57
|
+
outline: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.secondary-header-toc-toggle:hover {
|
|
61
|
+
color: var(--color-text-secondary);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.secondary-header-toc-toggle:focus-visible {
|
|
65
|
+
outline: 2px solid var(--color-primary);
|
|
66
|
+
outline-offset: 2px;
|
|
67
|
+
border-radius: var(--radius-sm);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.secondary-header-toc-toggle svg {
|
|
71
|
+
transform: rotate(-90deg);
|
|
72
|
+
transition: transform var(--transition-base);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.secondary-header-toc-toggle[aria-expanded="true"] svg {
|
|
76
|
+
transform: rotate(0deg);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* TOC Navigation */
|
|
81
|
+
.docyard-toc__nav {
|
|
82
|
+
display: block;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* TOC List */
|
|
86
|
+
.docyard-toc__list {
|
|
87
|
+
list-style: none;
|
|
88
|
+
padding: 0;
|
|
89
|
+
margin: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.docyard-toc__list .docyard-toc__list {
|
|
93
|
+
margin-top: var(--space-1);
|
|
94
|
+
margin-left: var(--space-4);
|
|
95
|
+
border-left: 1px solid var(--color-border);
|
|
96
|
+
padding-left: var(--space-3);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* TOC Items */
|
|
100
|
+
.docyard-toc__item {
|
|
101
|
+
margin-bottom: 0.375rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* TOC Links */
|
|
105
|
+
.docyard-toc__link {
|
|
106
|
+
display: block;
|
|
107
|
+
padding: 0.25rem var(--space-2);
|
|
108
|
+
color: var(--color-text-secondary);
|
|
109
|
+
text-decoration: none;
|
|
110
|
+
font-size: var(--font-size-sm);
|
|
111
|
+
line-height: var(--line-height-tight);
|
|
112
|
+
border-radius: var(--radius-sm);
|
|
113
|
+
transition: color var(--transition-fast);
|
|
114
|
+
font-weight: var(--font-weight-medium);
|
|
115
|
+
overflow: hidden;
|
|
116
|
+
text-overflow: ellipsis;
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.docyard-toc__link:hover {
|
|
121
|
+
color: var(--color-text);
|
|
122
|
+
text-decoration: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.docyard-toc__link:focus-visible {
|
|
126
|
+
outline: 2px solid var(--color-primary);
|
|
127
|
+
outline-offset: 2px;
|
|
128
|
+
color: var(--color-text);
|
|
129
|
+
text-decoration: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Active link */
|
|
133
|
+
.docyard-toc__link.is-active {
|
|
134
|
+
color: var(--color-primary);
|
|
135
|
+
font-weight: var(--font-weight-semibold);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Level-specific styling */
|
|
139
|
+
.docyard-toc__item--level-2>.docyard-toc__link {
|
|
140
|
+
font-weight: var(--font-weight-medium);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.docyard-toc__item--level-3>.docyard-toc__link {
|
|
144
|
+
font-size: 0.8125rem;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.docyard-toc__item--level-4>.docyard-toc__link {
|
|
148
|
+
font-size: 0.8125rem;
|
|
149
|
+
color: var(--color-text-tertiary);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* TOC Scrollbar */
|
|
153
|
+
.docyard-toc::-webkit-scrollbar {
|
|
154
|
+
width: 0.5rem;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.docyard-toc::-webkit-scrollbar-track {
|
|
158
|
+
background: transparent;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.docyard-toc::-webkit-scrollbar-thumb {
|
|
162
|
+
background: rgba(208, 215, 222, 0.5);
|
|
163
|
+
border-radius: 0.25rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.docyard-toc::-webkit-scrollbar-thumb:hover {
|
|
167
|
+
background: rgba(208, 215, 222, 0.7);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.dark .docyard-toc::-webkit-scrollbar-thumb {
|
|
171
|
+
background: rgba(110, 118, 129, 0.4);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.dark .docyard-toc::-webkit-scrollbar-thumb:hover {
|
|
175
|
+
background: rgba(110, 118, 129, 0.6);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Tablet & Mobile Layout - TOC in secondary header */
|
|
179
|
+
@media (max-width: 1280px) {
|
|
180
|
+
.docyard-toc {
|
|
181
|
+
display: block;
|
|
182
|
+
position: static;
|
|
183
|
+
width: 0;
|
|
184
|
+
height: 0;
|
|
185
|
+
padding: 0;
|
|
186
|
+
margin: 0;
|
|
187
|
+
border: none;
|
|
188
|
+
overflow: visible;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.docyard-toc__header {
|
|
192
|
+
display: none;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.docyard-toc__nav {
|
|
196
|
+
display: none;
|
|
197
|
+
position: fixed;
|
|
198
|
+
overflow-y: auto;
|
|
199
|
+
overscroll-behavior: contain;
|
|
200
|
+
padding: var(--space-5);
|
|
201
|
+
background: var(--color-bg);
|
|
202
|
+
border: 1px solid var(--color-border);
|
|
203
|
+
border-radius: var(--radius-lg);
|
|
204
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
205
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.06),
|
|
206
|
+
0 0 0 1px rgba(0, 0, 0, 0.05);
|
|
207
|
+
z-index: 9999;
|
|
208
|
+
transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
|
209
|
+
opacity 0.2s ease,
|
|
210
|
+
transform 0.2s ease;
|
|
211
|
+
transform: translateY(-8px);
|
|
212
|
+
opacity: 0;
|
|
213
|
+
pointer-events: none;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.dark .docyard-toc__nav {
|
|
217
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3),
|
|
218
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.2),
|
|
219
|
+
0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.docyard-toc__nav.is-expanded {
|
|
223
|
+
display: block;
|
|
224
|
+
opacity: 1;
|
|
225
|
+
transform: translateY(0);
|
|
226
|
+
pointer-events: auto;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Tablet-specific: floating dropdown on right */
|
|
231
|
+
@media (max-width: 1280px) and (min-width: 1025px) {
|
|
232
|
+
.docyard-toc__nav {
|
|
233
|
+
top: calc(var(--header-height) + 3rem + var(--space-2));
|
|
234
|
+
right: var(--space-6);
|
|
235
|
+
width: 280px;
|
|
236
|
+
max-height: calc(100vh - var(--header-height) - 3rem - var(--space-8));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Mobile Layout (< 1024px) */
|
|
241
|
+
@media (max-width: 1024px) {
|
|
242
|
+
.docyard-toc__nav {
|
|
243
|
+
top: calc(var(--header-height) + 3rem - 2px);
|
|
244
|
+
left: var(--space-4);
|
|
245
|
+
right: var(--space-4);
|
|
246
|
+
max-height: calc(100vh - var(--header-height) - 3rem - var(--space-2));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.secondary-header.shift-up~.layout .docyard-toc__nav {
|
|
250
|
+
top: calc(3rem - 2px);
|
|
251
|
+
max-height: calc(100vh - 3rem - var(--space-2));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.docyard-toc__list .docyard-toc__list {
|
|
255
|
+
margin-left: var(--space-3);
|
|
256
|
+
padding-left: var(--space-2);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Reduced Motion */
|
|
261
|
+
@media (prefers-reduced-motion: reduce) {
|
|
262
|
+
.docyard-toc__link {
|
|
263
|
+
transition: none;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.secondary-header-toc-toggle svg {
|
|
267
|
+
transition: none;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -65,11 +65,45 @@
|
|
|
65
65
|
border-radius: var(--radius-sm);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/* Secondary Header (Mobile Navigation Bar) */
|
|
68
|
+
/* Secondary Header (Tablet & Mobile Navigation Bar) */
|
|
69
69
|
.secondary-header {
|
|
70
70
|
display: none;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/* Tablet: Show secondary header with TOC toggle only */
|
|
74
|
+
@media (max-width: 1280px) and (min-width: 1025px) {
|
|
75
|
+
.secondary-header {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: flex-end;
|
|
79
|
+
position: fixed;
|
|
80
|
+
top: var(--header-height);
|
|
81
|
+
left: var(--sidebar-width);
|
|
82
|
+
right: 0;
|
|
83
|
+
height: 3rem;
|
|
84
|
+
min-height: 3rem;
|
|
85
|
+
background: var(--color-bg);
|
|
86
|
+
border-bottom: 1px solid var(--color-border);
|
|
87
|
+
padding: 0 var(--space-6);
|
|
88
|
+
z-index: var(--z-secondary-header);
|
|
89
|
+
transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1), left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
90
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.02);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.secondary-header.shift-up {
|
|
94
|
+
top: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.secondary-header-menu {
|
|
98
|
+
display: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.layout {
|
|
102
|
+
padding-top: calc(var(--header-height) + 3rem);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Mobile: Show menu button + TOC toggle */
|
|
73
107
|
@media (max-width: 1024px) {
|
|
74
108
|
.secondary-header {
|
|
75
109
|
display: flex;
|
|
@@ -80,6 +114,7 @@
|
|
|
80
114
|
left: 0;
|
|
81
115
|
right: 0;
|
|
82
116
|
height: 3rem;
|
|
117
|
+
min-height: 3rem;
|
|
83
118
|
background: var(--color-bg);
|
|
84
119
|
border-bottom: 1px solid var(--color-border);
|
|
85
120
|
padding: 0 var(--space-6);
|
|
@@ -107,6 +142,7 @@
|
|
|
107
142
|
transition: background-color 0.2s ease;
|
|
108
143
|
font-family: var(--font-sans);
|
|
109
144
|
margin-left: calc(-1 * var(--space-2));
|
|
145
|
+
outline: none;
|
|
110
146
|
}
|
|
111
147
|
|
|
112
148
|
.secondary-header-menu:hover {
|
|
@@ -117,6 +153,12 @@
|
|
|
117
153
|
background-color: var(--color-border);
|
|
118
154
|
}
|
|
119
155
|
|
|
156
|
+
.secondary-header-menu:focus-visible {
|
|
157
|
+
outline: 2px solid var(--color-primary);
|
|
158
|
+
outline-offset: 2px;
|
|
159
|
+
border-radius: var(--radius-sm);
|
|
160
|
+
}
|
|
161
|
+
|
|
120
162
|
.secondary-header-menu .icon {
|
|
121
163
|
flex-shrink: 0;
|
|
122
164
|
}
|
|
@@ -176,11 +218,19 @@
|
|
|
176
218
|
.layout-main {
|
|
177
219
|
flex: 1;
|
|
178
220
|
margin-left: var(--sidebar-width);
|
|
221
|
+
margin-right: var(--toc-width, 0);
|
|
179
222
|
display: flex;
|
|
180
223
|
flex-direction: column;
|
|
181
224
|
min-width: 0;
|
|
182
225
|
}
|
|
183
226
|
|
|
227
|
+
/* Tablet Layout (1024px - 1280px) */
|
|
228
|
+
@media (max-width: 1280px) and (min-width: 1025px) {
|
|
229
|
+
.layout-main {
|
|
230
|
+
margin-right: 0;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
184
234
|
/* Content wrapper */
|
|
185
235
|
main.content {
|
|
186
236
|
flex: 1;
|
|
@@ -200,6 +250,7 @@ main.content {
|
|
|
200
250
|
|
|
201
251
|
.layout {
|
|
202
252
|
padding-top: calc(var(--header-height) + 3rem);
|
|
253
|
+
flex-direction: column;
|
|
203
254
|
}
|
|
204
255
|
|
|
205
256
|
.sidebar {
|
|
@@ -223,8 +274,14 @@ main.content {
|
|
|
223
274
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
|
224
275
|
}
|
|
225
276
|
|
|
277
|
+
.docyard-toc {
|
|
278
|
+
order: 1;
|
|
279
|
+
}
|
|
280
|
+
|
|
226
281
|
.layout-main {
|
|
227
282
|
margin-left: 0;
|
|
283
|
+
margin-right: 0;
|
|
284
|
+
order: 2;
|
|
228
285
|
}
|
|
229
286
|
|
|
230
287
|
main.content {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HeadingAnchorManager handles anchor link interactions
|
|
3
|
+
* Provides copy-to-clipboard functionality with visual feedback
|
|
4
|
+
*/
|
|
5
|
+
class HeadingAnchorManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.anchors = document.querySelectorAll('.heading-anchor');
|
|
8
|
+
this.init();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
init() {
|
|
12
|
+
this.anchors.forEach(anchor => {
|
|
13
|
+
anchor.addEventListener('click', (e) => this.handleClick(e, anchor));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Handle anchor link click
|
|
19
|
+
* @param {Event} e - Click event
|
|
20
|
+
* @param {HTMLElement} anchor - Anchor element
|
|
21
|
+
*/
|
|
22
|
+
handleClick(e, anchor) {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
|
|
25
|
+
const headingId = anchor.dataset.headingId;
|
|
26
|
+
const url = `${window.location.origin}${window.location.pathname}#${headingId}`;
|
|
27
|
+
|
|
28
|
+
this.copyToClipboard(url, anchor);
|
|
29
|
+
|
|
30
|
+
history.pushState(null, null, `#${headingId}`);
|
|
31
|
+
|
|
32
|
+
const heading = document.getElementById(headingId);
|
|
33
|
+
if (heading) {
|
|
34
|
+
heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Copy text to clipboard with visual feedback
|
|
40
|
+
* @param {string} text - Text to copy
|
|
41
|
+
* @param {HTMLElement} anchor - Anchor element for feedback
|
|
42
|
+
*/
|
|
43
|
+
async copyToClipboard(text, anchor) {
|
|
44
|
+
try {
|
|
45
|
+
await navigator.clipboard.writeText(text);
|
|
46
|
+
this.showFeedback(anchor, true);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
this.fallbackCopyToClipboard(text);
|
|
49
|
+
this.showFeedback(anchor, true);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Fallback copy method for older browsers
|
|
55
|
+
* @param {string} text - Text to copy
|
|
56
|
+
*/
|
|
57
|
+
fallbackCopyToClipboard(text) {
|
|
58
|
+
const textarea = document.createElement('textarea');
|
|
59
|
+
textarea.value = text;
|
|
60
|
+
textarea.style.position = 'fixed';
|
|
61
|
+
textarea.style.opacity = '0';
|
|
62
|
+
document.body.appendChild(textarea);
|
|
63
|
+
textarea.select();
|
|
64
|
+
document.execCommand('copy');
|
|
65
|
+
document.body.removeChild(textarea);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Show visual feedback on copy
|
|
70
|
+
* @param {HTMLElement} anchor - Anchor element
|
|
71
|
+
* @param {boolean} success - Whether copy succeeded
|
|
72
|
+
*/
|
|
73
|
+
showFeedback(anchor, success) {
|
|
74
|
+
const originalTitle = anchor.getAttribute('aria-label');
|
|
75
|
+
anchor.setAttribute('aria-label', success ? 'Link copied!' : 'Failed to copy');
|
|
76
|
+
|
|
77
|
+
anchor.style.color = success ? 'var(--color-success, #10b981)' : 'var(--color-danger, #ef4444)';
|
|
78
|
+
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
anchor.setAttribute('aria-label', originalTitle);
|
|
81
|
+
anchor.style.color = '';
|
|
82
|
+
}, 2000);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (typeof window !== 'undefined') {
|
|
87
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
88
|
+
new HeadingAnchorManager();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
const sidebar = document.querySelector('.sidebar');
|
|
10
10
|
const overlay = document.querySelector('.mobile-overlay');
|
|
11
11
|
|
|
12
|
-
if (!toggle || !sidebar || !overlay)
|
|
12
|
+
if (!toggle || !sidebar || !overlay) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
13
15
|
|
|
14
16
|
function openMenu() {
|
|
15
17
|
sidebar.classList.add('is-open');
|
|
@@ -33,7 +35,9 @@
|
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
toggle.addEventListener('click',
|
|
38
|
+
toggle.addEventListener('click', function(e) {
|
|
39
|
+
toggleMenu();
|
|
40
|
+
});
|
|
37
41
|
overlay.addEventListener('click', closeMenu);
|
|
38
42
|
|
|
39
43
|
document.addEventListener('keydown', function(e) {
|