website
git clone https://git.pyrossh.dev/website
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
44991dd
— pyrossh
2026-08-04T20:19:48+05:30
remove stuff
- assets/css/markdown.css +70 -53
- assets/css/repos.css +334 -346
- assets/css/shared.css +149 -130
- assets/css/workers/cv.css +9 -2
- assets/css/workers/home.css +169 -164
- eleventy.config.js +56 -28
- patches/@elenajs+ssr+1.0.0-alpha.11.patch +30 -2
- src/_data/localRepos.js +2 -1
- src/_helpers/highlightCode.js +42 -11
- src/_includes/file-layout.njk +13 -0
- src/_includes/layout.njk +3 -5
- src/_includes/post.njk +1 -1
- src/_includes/repo-layout.njk +16 -0
- src/_includes/site-footer.njk +11 -0
- src/_includes/site-header.njk +14 -0
- src/components/code-view.js +0 -18
- src/components/current-year.js +0 -11
- src/components/file-layout.js +0 -42
- src/components/markdown-view.js +0 -18
- src/components/repo-layout.js +0 -50
- src/components/site-footer.js +0 -22
- src/components/site-header.js +0 -29
- src/cv.njk +5 -6
- src/index.njk +0 -1
- src/only-bible-app/index.njk +0 -1
- src/only-bible-app/privacy-policy.njk +0 -1
- src/only-bible-app/terms-and-conditions.njk +0 -1
- src/posts.njk +0 -1
- src/repos/commit.njk +2 -5
- src/repos/commits.njk +2 -5
- src/repos/file-blame.njk +4 -6
- src/repos/file-history.njk +4 -6
- src/repos/file.njk +5 -7
- src/repos/files-index.njk +2 -5
- src/repos/repo.njk +3 -6
assets/css/markdown.css
CHANGED
|
@@ -1,62 +1,79 @@
|
|
|
1
1
|
/* Shared typography for rendered markdown content (blog posts + repo
|
|
2
|
-
READMEs) — anything wrapped in <markdown-
|
|
2
|
+
READMEs) — anything wrapped in a plain <article class="markdown-body">
|
|
3
|
-
|
|
3
|
+
(see repo.njk/post.njk; no wrapping custom element anymore). Extracted
|
|
4
|
-
|
|
4
|
+
from the old posts-detail.css so repo pages get the same look instead
|
|
5
|
+
of no styling at all. Deliberately scoped to .markdown-body p/li code
|
|
5
|
-
|
|
6
|
+
(inline code), not .markdown-body code generally — a bare `code`
|
|
6
|
-
|
|
7
|
+
selector would also catch the <code> inside fenced <pre> blocks and
|
|
8
|
+
stomp the syntax-highlighting styles from shared.css with the small
|
|
7
|
-
|
|
9
|
+
inline-code padding/background (that shared styling is cross-cutting
|
|
8
|
-
|
|
10
|
+
between markdown content and the file-viewer, so it stays global
|
|
9
|
-
|
|
11
|
+
rather than duplicated here). */
|
|
10
|
-
.markdown-body
|
|
12
|
+
.markdown-body {
|
|
11
|
-
font-size: 1.
|
|
13
|
+
font-size: 1.1rem;
|
|
12
|
-
|
|
14
|
+
line-height: 1.5;
|
|
13
|
-
margin-top: 0.5rem;
|
|
14
|
-
margin-bottom: 0.5rem;
|
|
15
|
-
}
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
h1 {
|
|
18
|
-
|
|
17
|
+
font-size: 1.5rem;
|
|
19
|
-
|
|
18
|
+
font-weight: 700;
|
|
20
|
-
|
|
19
|
+
margin-top: 0;
|
|
21
|
-
|
|
20
|
+
margin-bottom: 0.5rem;
|
|
22
|
-
}
|
|
21
|
+
}
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
h2 {
|
|
25
|
-
|
|
24
|
+
font-size: 1.5rem;
|
|
26
|
-
|
|
25
|
+
font-weight: 700;
|
|
27
|
-
|
|
26
|
+
margin-top: 0;
|
|
28
|
-
|
|
27
|
+
margin-bottom: 0.5rem;
|
|
29
|
-
}
|
|
28
|
+
}
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
h3 {
|
|
32
|
-
|
|
31
|
+
font-size: 1.25rem;
|
|
32
|
+
font-weight: 700;
|
|
33
|
+
margin-top: 0.5rem;
|
|
33
|
-
|
|
34
|
+
margin-bottom: 0.1rem;
|
|
34
|
-
}
|
|
35
|
+
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
p {
|
|
37
|
-
|
|
38
|
+
font-size: 1.1rem;
|
|
39
|
+
padding-bottom: 1rem;
|
|
38
|
-
}
|
|
40
|
+
}
|
|
39
41
|
|
|
40
|
-
.markdown-body p code,
|
|
41
|
-
|
|
42
|
+
li {
|
|
42
|
-
color: var(--color-code-fg) !important;
|
|
43
|
-
background: var(--color-code-bg) !important;
|
|
44
|
-
word-wrap: break-word;
|
|
45
|
-
box-decoration-break: clone;
|
|
46
|
-
padding: 2px 4px;
|
|
47
|
-
border-radius: 0.2rem;
|
|
48
|
-
font-weight: 400;
|
|
49
|
-
|
|
43
|
+
font-size: 1.1rem;
|
|
50
|
-
}
|
|
44
|
+
}
|
|
51
45
|
|
|
46
|
+
p code,
|
|
47
|
+
li code {
|
|
48
|
+
color: var(--color-code-fg) !important;
|
|
49
|
+
background: var(--color-code-bg) !important;
|
|
50
|
+
word-wrap: break-word;
|
|
51
|
+
box-decoration-break: clone;
|
|
52
|
+
padding: 2px 4px;
|
|
53
|
+
border-radius: 0.2rem;
|
|
54
|
+
font-weight: 400;
|
|
55
|
+
font-size: 0.8rem;
|
|
56
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Fenced code blocks in a README (markdown-it output — no language-
|
|
60
|
+
class on the <pre> itself, unlike Prism's blog-post output, which is
|
|
61
|
+
how this targets only the README case) get a subtle border + corner
|
|
62
|
+
radius, matching the framed-code-block look. */
|
|
63
|
+
pre:not([class*="language-"]) {
|
|
64
|
+
border: 1px solid var(--color-box-bg);
|
|
65
|
+
border-radius: 0.4rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
52
|
-
|
|
68
|
+
blockquote {
|
|
53
|
-
|
|
69
|
+
font-size: 1.2rem;
|
|
54
|
-
|
|
70
|
+
font-weight: 600;
|
|
55
|
-
|
|
71
|
+
background: var(--color-highlight-bg);
|
|
56
|
-
|
|
72
|
+
padding: 0.4rem;
|
|
57
|
-
|
|
73
|
+
margin: 16px 0px;
|
|
58
|
-
}
|
|
59
74
|
|
|
60
|
-
|
|
75
|
+
p {
|
|
61
|
-
|
|
76
|
+
padding: 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
62
79
|
}
|
assets/css/repos.css
CHANGED
|
@@ -6,134 +6,116 @@
|
|
|
6
6
|
|
|
7
7
|
Font sizes on this page follow one small scale instead of one-off
|
|
8
8
|
values, so nothing reads as randomly bigger/smaller than its neighbour:
|
|
9
|
-
0.8rem — code/monospace content (pre/code-view, file tree, blame)
|
|
10
9
|
0.85rem — small badges (tags, pull-right meta, file-size hints)
|
|
11
|
-
0.9rem — nav links and
|
|
10
|
+
0.9rem — code/monospace content, nav links and sub-headings alike
|
|
11
|
+
(pre/code-view, file tree, blame, h3, file title)
|
|
12
12
|
0.95rem — box body text (.repo, .event)
|
|
13
13
|
1.6rem — page-level heading (.repo h1)
|
|
14
14
|
All of them are rem-based, not em, so nesting depth can't change the
|
|
15
15
|
rendered size — that's what made the old code-view/blame font sizes
|
|
16
|
-
inconsistent (em compounds with whatever ancestor happens to wrap it).
|
|
16
|
+
inconsistent (em compounds with whatever ancestor happens to wrap it).
|
|
17
|
+
|
|
18
|
+
Everything below uses plain CSS nesting rather than @scope: .repo,
|
|
19
|
+
.file-layout, .event, .file-explorer and table.blame are all real,
|
|
20
|
+
always-present classes/tags on these pages (none of them are wrapped
|
|
21
|
+
in a custom element anymore), so nesting under them scopes their
|
|
22
|
+
descendants exactly the way @scope would, without needing a scope root
|
|
23
|
+
selector that has to be kept in sync with whatever markup happens to
|
|
24
|
+
wrap it. */
|
|
17
25
|
|
|
18
26
|
.repo {
|
|
19
27
|
text-overflow: ellipsis;
|
|
20
28
|
overflow: hidden;
|
|
21
|
-
padding:
|
|
29
|
+
padding: 0.5rem;
|
|
22
30
|
background: var(--color-box-bg);
|
|
23
31
|
color: var(--color-text);
|
|
24
32
|
font-size: 0.95rem;
|
|
25
|
-
border-radius: 6px;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.repo a {
|
|
29
|
-
color: var(--color-link);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.repo .header {
|
|
33
|
-
display: flex;
|
|
34
|
-
flex-direction: row;
|
|
35
|
-
align-items: baseline;
|
|
36
|
-
}
|
|
37
33
|
|
|
38
|
-
@media (max-width: 720px) {
|
|
39
|
-
|
|
34
|
+
a {
|
|
40
|
-
|
|
35
|
+
color: var(--color-link);
|
|
41
|
-
align-items: start;
|
|
42
36
|
}
|
|
43
|
-
}
|
|
44
37
|
|
|
45
|
-
.
|
|
38
|
+
.header {
|
|
39
|
+
display: flex;
|
|
46
|
-
|
|
40
|
+
flex-direction: row;
|
|
47
|
-
font-weight: 500;
|
|
48
|
-
line-height: 1;
|
|
49
|
-
|
|
41
|
+
align-items: baseline;
|
|
50
|
-
flex: 1;
|
|
51
|
-
}
|
|
52
42
|
|
|
53
|
-
|
|
43
|
+
@media (max-width: 720px) {
|
|
54
|
-
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
align-items: start;
|
|
55
|
-
}
|
|
46
|
+
}
|
|
56
|
-
|
|
57
|
-
.repo .tags {
|
|
58
|
-
margin-top: 0.5rem;
|
|
59
|
-
display: flex;
|
|
60
|
-
flex-wrap: wrap;
|
|
61
|
-
gap: 0.35rem;
|
|
62
|
-
}
|
|
47
|
+
}
|
|
63
|
-
|
|
64
|
-
.repo .tag {
|
|
65
|
-
color: var(--color-tag);
|
|
66
|
-
background: var(--color-pre-bg);
|
|
67
|
-
padding: 0.15rem 0.55rem;
|
|
68
|
-
border-radius: 999px;
|
|
69
|
-
font-size: 0.85rem;
|
|
70
|
-
font-weight: 500;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.repo h3 {
|
|
74
|
-
font-size: 0.9rem;
|
|
75
|
-
font-weight: 500;
|
|
76
|
-
margin-top: 0.75rem;
|
|
77
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
78
|
-
background: var(--color-pre-bg);
|
|
79
|
-
color: var(--color-code-fg);
|
|
80
|
-
padding: 0.5rem 0.75rem;
|
|
81
|
-
border-radius: 4px;
|
|
82
|
-
overflow-x: auto;
|
|
83
|
-
white-space: nowrap;
|
|
84
|
-
}
|
|
85
48
|
|
|
86
|
-
|
|
49
|
+
h1 {
|
|
87
|
-
|
|
50
|
+
font-size: 1.6rem;
|
|
51
|
+
font-weight: 500;
|
|
52
|
+
line-height: 1;
|
|
53
|
+
margin-bottom: 0.1rem;
|
|
88
|
-
|
|
54
|
+
flex: 1;
|
|
89
|
-
padding-top: 0.6rem;
|
|
90
|
-
font-size: 0.95rem;
|
|
91
|
-
font-weight: 400;
|
|
92
|
-
}
|
|
55
|
+
}
|
|
93
56
|
|
|
94
|
-
.
|
|
57
|
+
.tags {
|
|
95
|
-
|
|
58
|
+
margin-top: 0.5rem;
|
|
96
|
-
border: none;
|
|
97
|
-
border-top: 1px solid var(--color-text);
|
|
98
|
-
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-wrap: wrap;
|
|
61
|
+
gap: 0.25rem;
|
|
99
|
-
}
|
|
62
|
+
}
|
|
100
63
|
|
|
101
|
-
.
|
|
64
|
+
.tag {
|
|
65
|
+
color: var(--color-tag);
|
|
66
|
+
padding: 0.15rem 0.5rem 0.5rem 0;
|
|
102
|
-
|
|
67
|
+
border-radius: 4px;
|
|
103
|
-
align-items: center;
|
|
104
|
-
gap: 0.25rem;
|
|
105
|
-
|
|
68
|
+
font-size: 0.95rem;
|
|
69
|
+
font-weight: 500;
|
|
106
|
-
}
|
|
70
|
+
}
|
|
107
71
|
|
|
108
|
-
|
|
72
|
+
h3 {
|
|
109
|
-
|
|
73
|
+
font-size: 1rem;
|
|
74
|
+
font-weight: 500;
|
|
110
|
-
}
|
|
75
|
+
}
|
|
111
76
|
|
|
112
|
-
.
|
|
77
|
+
.headerExtension h2 {
|
|
78
|
+
display: flex;
|
|
79
|
+
flex: 1;
|
|
80
|
+
padding-top: 0.3rem;
|
|
113
|
-
|
|
81
|
+
font-size: 0.95rem;
|
|
114
|
-
|
|
82
|
+
font-weight: 400;
|
|
115
|
-
padding: 0.4rem 0.1rem;
|
|
116
|
-
cursor: pointer;
|
|
117
|
-
color: var(--color-text);
|
|
118
|
-
border-bottom: 2px solid transparent;
|
|
119
|
-
margin-right: 1rem;
|
|
120
|
-
}
|
|
83
|
+
}
|
|
121
84
|
|
|
85
|
+
hr {
|
|
122
|
-
|
|
86
|
+
margin-top: 0.5rem;
|
|
87
|
+
border: none;
|
|
123
|
-
|
|
88
|
+
border-top: 1px solid var(--color-text);
|
|
89
|
+
opacity: 0.15;
|
|
124
|
-
}
|
|
90
|
+
}
|
|
125
91
|
|
|
92
|
+
.nav {
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
margin-top: 0.5rem;
|
|
96
|
+
|
|
97
|
+
span {
|
|
98
|
+
display: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
a {
|
|
102
|
+
font-size: 1rem;
|
|
103
|
+
font-weight: 500;
|
|
126
|
-
|
|
104
|
+
padding: 0 0.5rem 0 0;
|
|
105
|
+
cursor: pointer;
|
|
127
|
-
|
|
106
|
+
color: var(--color-link);
|
|
128
|
-
border-bottom-color: var(--color-link);
|
|
129
|
-
|
|
107
|
+
text-decoration: underline;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
130
110
|
}
|
|
131
111
|
|
|
132
112
|
#repo-content {
|
|
133
|
-
margin-top:
|
|
113
|
+
margin-top: 0.25rem;
|
|
134
114
|
}
|
|
135
115
|
|
|
136
|
-
/* Commit list / commit detail header (Commit.astro)
|
|
116
|
+
/* Commit list / commit detail header (Commit.astro) — repos/commit.njk,
|
|
117
|
+
commits.njk, file-history.njk. pre.commit is nested here too since
|
|
118
|
+
it's always .event's child, never used alone. */
|
|
137
119
|
.event {
|
|
138
120
|
background-color: var(--color-box-bg);
|
|
139
121
|
color: var(--color-text);
|
|
@@ -142,27 +124,26 @@
|
|
|
142
124
|
padding: 0.6rem 0.75rem;
|
|
143
125
|
margin: 0.5rem 0;
|
|
144
126
|
font-size: 0.95rem;
|
|
145
|
-
border-radius: 4px;
|
|
146
|
-
}
|
|
147
127
|
|
|
148
|
-
|
|
128
|
+
a {
|
|
149
|
-
|
|
129
|
+
color: var(--color-link);
|
|
150
|
-
}
|
|
130
|
+
}
|
|
151
131
|
|
|
152
|
-
|
|
132
|
+
strong {
|
|
153
|
-
|
|
133
|
+
font-weight: 500;
|
|
154
|
-
}
|
|
134
|
+
}
|
|
155
135
|
|
|
156
|
-
.
|
|
136
|
+
.pull-right {
|
|
157
|
-
|
|
137
|
+
font-size: 0.85rem;
|
|
158
|
-
|
|
138
|
+
font-weight: 400;
|
|
159
|
-
|
|
139
|
+
float: right;
|
|
160
|
-
}
|
|
140
|
+
}
|
|
161
141
|
|
|
162
|
-
pre.commit {
|
|
142
|
+
pre.commit {
|
|
163
|
-
|
|
143
|
+
font-family: inherit;
|
|
164
|
-
|
|
144
|
+
white-space: pre-wrap;
|
|
165
|
-
|
|
145
|
+
margin-top: 0.35rem;
|
|
146
|
+
}
|
|
166
147
|
}
|
|
167
148
|
|
|
168
149
|
/* Two-pane IDE-style file explorer: a persistent tree sidebar + a content
|
|
@@ -171,8 +152,9 @@ pre.commit {
|
|
|
171
152
|
they don't also need to differ in colour. The tree lives outside
|
|
172
153
|
#file-pane, so htmx swaps of #file-pane (clicking a file) or
|
|
173
154
|
#file-content (Contents/History/Blame) never touch the sidebar — it
|
|
174
|
-
just stays where it is, already expanded, no reload.
|
|
155
|
+
just stays where it is, already expanded, no reload.
|
|
156
|
+
|
|
175
|
-
|
|
157
|
+
Fixed-height explorer: both panes stretch to the same height and scroll
|
|
176
158
|
independently, so scrolling a long file never scrolls the tree out of
|
|
177
159
|
view (or the whole page) — closer to a real IDE split view than a page
|
|
178
160
|
that just keeps growing with the file's content. */
|
|
@@ -181,139 +163,167 @@ pre.commit {
|
|
|
181
163
|
align-items: stretch;
|
|
182
164
|
gap: 1.25rem;
|
|
183
165
|
height: 80vh;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.file-explorer-tree {
|
|
187
|
-
width: 260px;
|
|
188
|
-
flex-shrink: 0;
|
|
189
|
-
height: 100%;
|
|
190
|
-
overflow-y: auto;
|
|
191
|
-
padding: 0.6rem;
|
|
192
|
-
background: var(--color-box-bg);
|
|
193
|
-
border-radius: 6px;
|
|
194
|
-
font-size: 0.8rem;
|
|
195
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.file-explorer-main {
|
|
199
|
-
flex: 1;
|
|
200
|
-
min-width: 0;
|
|
201
|
-
height: 100%;
|
|
202
|
-
display: flex;
|
|
203
|
-
flex-direction: column;
|
|
204
|
-
border: 1px solid var(--color-box-bg);
|
|
205
|
-
border-radius: 6px;
|
|
206
|
-
overflow: hidden;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.file-explorer-main:has(.file-explorer-placeholder) {
|
|
210
|
-
align-items: center;
|
|
211
|
-
justify-content: center;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.file-explorer-placeholder {
|
|
215
|
-
color: var(--color-text);
|
|
216
|
-
opacity: 0.6;
|
|
217
|
-
padding: 1rem 0;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
@media (max-width: 720px) {
|
|
221
|
-
.file-explorer {
|
|
222
|
-
flex-direction: column;
|
|
223
|
-
height: 85vh;
|
|
224
|
-
}
|
|
225
166
|
|
|
167
|
+
/* File tree: flat rows (no per-item card background), a rotating
|
|
168
|
+
triangle for expand/collapse instead of an emoji folder glyph, and a
|
|
169
|
+
left accent border + tint on the active file instead of a filled
|
|
170
|
+
pill. */
|
|
226
171
|
.file-explorer-tree {
|
|
172
|
+
width: 260px;
|
|
173
|
+
flex-shrink: 0;
|
|
174
|
+
height: 100%;
|
|
175
|
+
overflow-y: auto;
|
|
176
|
+
padding: 0.6rem;
|
|
177
|
+
background: var(--color-box-bg);
|
|
178
|
+
border-radius: 6px;
|
|
179
|
+
font-size: 0.9rem;
|
|
180
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
181
|
+
|
|
182
|
+
ul {
|
|
183
|
+
list-style: none;
|
|
184
|
+
margin: 0 0 0 1.1rem;
|
|
185
|
+
padding: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
> ul {
|
|
189
|
+
margin-left: 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
li {
|
|
193
|
+
margin: 0;
|
|
194
|
+
|
|
195
|
+
&.active-file,
|
|
196
|
+
& > a.active-file {
|
|
197
|
+
color: var(--color-link);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
summary,
|
|
202
|
+
li > a {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: 0.4rem;
|
|
206
|
+
padding: 0.2rem 0.35rem;
|
|
207
|
+
border-radius: 4px;
|
|
208
|
+
border-left: 2px solid transparent;
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
text-decoration: none;
|
|
211
|
+
color: var(--color-text);
|
|
212
|
+
line-height: 1.6;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
summary {
|
|
216
|
+
list-style: none;
|
|
217
|
+
|
|
218
|
+
&::-webkit-details-marker {
|
|
219
|
+
display: none;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&::before {
|
|
223
|
+
content: "";
|
|
224
|
+
flex-shrink: 0;
|
|
225
|
+
width: 0;
|
|
226
|
+
height: 0;
|
|
227
|
+
border-style: solid;
|
|
228
|
+
border-width: 0.3em 0 0.3em 0.4em;
|
|
229
|
+
border-color: transparent transparent transparent currentColor;
|
|
230
|
+
opacity: 0.5;
|
|
231
|
+
transition: transform 120ms ease;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
details[open] > summary::before {
|
|
236
|
+
transform: rotate(90deg);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
summary svg,
|
|
240
|
+
li > a svg {
|
|
241
|
+
flex-shrink: 0;
|
|
227
|
-
|
|
242
|
+
width: 1em;
|
|
228
|
-
|
|
243
|
+
height: 1em;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
summary:hover,
|
|
247
|
+
li > a:hover {
|
|
248
|
+
background-color: var(--color-pre-bg);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
li > a.active-file {
|
|
252
|
+
color: var(--color-link);
|
|
253
|
+
background-color: var(--color-highlight-bg);
|
|
254
|
+
border-left-color: var(--color-link);
|
|
255
|
+
font-weight: 500;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.file-size {
|
|
259
|
+
margin-left: auto;
|
|
229
|
-
|
|
260
|
+
opacity: 0.5;
|
|
261
|
+
font-size: 0.85rem;
|
|
262
|
+
}
|
|
230
263
|
}
|
|
231
264
|
|
|
232
265
|
.file-explorer-main {
|
|
266
|
+
flex: 1;
|
|
233
|
-
width:
|
|
267
|
+
min-width: 0;
|
|
234
|
-
height:
|
|
268
|
+
height: 100%;
|
|
269
|
+
display: flex;
|
|
270
|
+
flex-direction: column;
|
|
271
|
+
border: 1px solid var(--color-box-bg);
|
|
272
|
+
border-radius: 6px;
|
|
273
|
+
overflow: hidden;
|
|
274
|
+
|
|
275
|
+
&:has(.file-explorer-placeholder) {
|
|
276
|
+
align-items: center;
|
|
277
|
+
justify-content: center;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* #file-content padding/background: when it holds actual code (a
|
|
281
|
+
plain <pre>), let the box-bg colour fill the whole pane
|
|
282
|
+
edge-to-edge instead of floating as a small inset card — closer to
|
|
283
|
+
a real code viewer's continuous code surface. box-bg (not pre-bg)
|
|
284
|
+
so it reads as one calm surface with the file-layout header above
|
|
285
|
+
it, rather than the near-black pre-bg used for one-off code
|
|
286
|
+
snippets elsewhere on the site. */
|
|
287
|
+
#file-content {
|
|
235
|
-
|
|
288
|
+
flex: 1;
|
|
289
|
+
overflow-y: auto;
|
|
290
|
+
padding: 0.85rem;
|
|
291
|
+
|
|
292
|
+
&:has(> pre) {
|
|
293
|
+
padding: 0;
|
|
294
|
+
background: var(--color-box-bg);
|
|
295
|
+
|
|
296
|
+
pre {
|
|
297
|
+
margin: 0;
|
|
298
|
+
background: transparent;
|
|
299
|
+
border-radius: 0;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
236
303
|
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/* File tree: flat rows (no per-item card background), a rotating
|
|
240
|
-
triangle for expand/collapse instead of an emoji folder glyph, and a
|
|
241
|
-
left accent border + tint on the active file instead of a filled pill. */
|
|
242
|
-
.file-explorer-tree ul {
|
|
243
|
-
list-style: none;
|
|
244
|
-
margin: 0 0 0 1.1rem;
|
|
245
|
-
padding: 0;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.file-explorer-tree > ul {
|
|
249
|
-
margin-left: 0;
|
|
250
|
-
}
|
|
251
304
|
|
|
252
|
-
.file-explorer-
|
|
305
|
+
.file-explorer-placeholder {
|
|
306
|
+
color: var(--color-text);
|
|
307
|
+
opacity: 0.6;
|
|
253
|
-
|
|
308
|
+
padding: 1rem 0;
|
|
254
|
-
}
|
|
309
|
+
}
|
|
255
|
-
|
|
256
|
-
.file-explorer-tree summary,
|
|
257
|
-
.file-explorer-tree li > a {
|
|
258
|
-
display: flex;
|
|
259
|
-
align-items: center;
|
|
260
|
-
gap: 0.4rem;
|
|
261
|
-
padding: 0.2rem 0.35rem;
|
|
262
|
-
border-radius: 4px;
|
|
263
|
-
border-left: 2px solid transparent;
|
|
264
|
-
cursor: pointer;
|
|
265
|
-
text-decoration: none;
|
|
266
|
-
color: var(--color-text);
|
|
267
|
-
line-height: 1.6;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.file-explorer-tree summary {
|
|
271
|
-
list-style: none;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
.file-explorer-tree summary::-webkit-details-marker {
|
|
275
|
-
display: none;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
.file-explorer-tree summary::before {
|
|
279
|
-
content: "";
|
|
280
|
-
flex-shrink: 0;
|
|
281
|
-
width: 0;
|
|
282
|
-
height: 0;
|
|
283
|
-
border-style: solid;
|
|
284
|
-
border-width: 0.3em 0 0.3em 0.4em;
|
|
285
|
-
border-color: transparent transparent transparent currentColor;
|
|
286
|
-
opacity: 0.5;
|
|
287
|
-
transition: transform 120ms ease;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.file-explorer-tree details[open] > summary::before {
|
|
291
|
-
transform: rotate(90deg);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.file-explorer-tree summary svg,
|
|
295
|
-
.file-explorer-tree li > a svg {
|
|
296
|
-
flex-shrink: 0;
|
|
297
|
-
width: 1em;
|
|
298
|
-
height: 1em;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
.file-explorer-tree summary:hover,
|
|
302
|
-
.file-explorer-tree li > a:hover {
|
|
303
|
-
background-color: var(--color-pre-bg);
|
|
304
|
-
}
|
|
305
310
|
|
|
306
|
-
.file-explorer-tree li > a.active-file {
|
|
307
|
-
|
|
311
|
+
@media (max-width: 720px) {
|
|
308
|
-
background-color: var(--color-highlight-bg);
|
|
309
|
-
|
|
312
|
+
flex-direction: column;
|
|
310
|
-
|
|
313
|
+
height: 85vh;
|
|
311
|
-
}
|
|
312
314
|
|
|
313
|
-
.file-explorer-tree
|
|
315
|
+
.file-explorer-tree {
|
|
316
|
+
width: 100%;
|
|
314
|
-
|
|
317
|
+
height: auto;
|
|
318
|
+
flex: 0 0 35%;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.file-explorer-main {
|
|
315
|
-
|
|
322
|
+
width: 100%;
|
|
323
|
+
height: auto;
|
|
316
|
-
|
|
324
|
+
flex: 1 1 auto;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
317
327
|
}
|
|
318
328
|
|
|
319
329
|
/* File view header — a compact icon+filename strip with the
|
|
@@ -323,128 +333,104 @@ pre.commit {
|
|
|
323
333
|
display: flex;
|
|
324
334
|
align-items: center;
|
|
325
335
|
justify-content: flex-start;
|
|
326
|
-
flex-wrap: wrap;
|
|
327
336
|
flex-shrink: 0;
|
|
328
337
|
gap: 0.5rem;
|
|
329
338
|
padding: 0.6rem 0.85rem;
|
|
330
339
|
background: var(--color-box-bg);
|
|
331
340
|
border-bottom: 1px solid var(--color-pre-bg);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.file-layout .title {
|
|
335
|
-
display: flex;
|
|
336
|
-
align-items: center;
|
|
337
|
-
gap: 0.4rem;
|
|
338
|
-
min-width: 0;
|
|
339
|
-
font-size: 0.8rem;
|
|
340
|
-
font-weight: 500;
|
|
341
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
342
|
-
}
|
|
343
341
|
|
|
342
|
+
.title {
|
|
343
|
+
display: flex;
|
|
344
|
-
|
|
344
|
+
align-items: center;
|
|
345
|
+
gap: 0.4rem;
|
|
346
|
+
/* flex:1 (not the default 0 1 auto) so it's the one that gives up
|
|
347
|
+
space when the row is tight — without this, a long filename could
|
|
348
|
+
force the row to wrap .nav onto its own line instead of shrinking,
|
|
349
|
+
since the wrap decision is made off each item's unshrunk
|
|
350
|
+
hypothetical width, not its post-shrink width. */
|
|
351
|
+
flex: 1 1 auto;
|
|
352
|
+
min-width: 0;
|
|
353
|
+
font-size: 0.9rem;
|
|
354
|
+
font-weight: 500;
|
|
355
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
356
|
+
|
|
357
|
+
svg {
|
|
345
|
-
|
|
358
|
+
flex-shrink: 0;
|
|
346
|
-
|
|
359
|
+
width: 1em;
|
|
347
|
-
|
|
360
|
+
height: 1em;
|
|
348
|
-
}
|
|
361
|
+
}
|
|
349
|
-
|
|
362
|
+
|
|
350
|
-
|
|
363
|
+
span {
|
|
351
|
-
|
|
364
|
+
overflow: hidden;
|
|
352
|
-
|
|
365
|
+
text-overflow: ellipsis;
|
|
353
|
-
|
|
366
|
+
white-space: nowrap;
|
|
354
|
-
}
|
|
367
|
+
}
|
|
355
|
-
|
|
356
|
-
.file-layout .nav {
|
|
357
|
-
display: flex;
|
|
358
|
-
align-items: center;
|
|
359
|
-
gap: 0.25rem;
|
|
360
|
-
/* Pin to the right edge of the header row regardless of wrap — with
|
|
361
|
-
justify-content alone on the parent, a long filename forcing a wrap
|
|
362
|
-
would leave the tabs left-aligned on their own line. */
|
|
363
|
-
margin-left: auto;
|
|
364
|
-
}
|
|
368
|
+
}
|
|
365
|
-
|
|
366
|
-
.file-layout .nav div:not(:has(a)) {
|
|
367
|
-
display: none;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
.file-layout .nav a {
|
|
371
|
-
font-size: 0.9rem;
|
|
372
|
-
font-weight: 500;
|
|
373
|
-
padding: 0.3rem 0.1rem;
|
|
374
|
-
margin-right: 0.75rem;
|
|
375
|
-
cursor: pointer;
|
|
376
|
-
color: var(--color-text);
|
|
377
|
-
border-bottom: 2px solid transparent;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.file-layout .nav a:hover {
|
|
381
|
-
color: var(--color-link);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.file-layout .nav a[aria-current="true"] {
|
|
385
|
-
color: var(--color-link);
|
|
386
|
-
border-bottom-color: var(--color-link);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.file-explorer-main #file-content {
|
|
390
|
-
flex: 1;
|
|
391
|
-
overflow-y: auto;
|
|
392
|
-
padding: 0.85rem;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/* Code panel background: when #file-content holds actual code (a plain
|
|
396
|
-
<pre> or the code-view custom element wrapping one), let the box-bg
|
|
397
|
-
colour fill the whole pane edge-to-edge instead of floating as a small
|
|
398
|
-
inset card — closer to a real code viewer's continuous code surface.
|
|
399
|
-
box-bg (not pre-bg) so it reads as one calm surface with the header
|
|
400
|
-
above it, rather than the near-black pre-bg used for one-off code
|
|
401
|
-
snippets elsewhere on the site. */
|
|
402
|
-
.file-explorer-main #file-content:has(> pre),
|
|
403
|
-
.file-explorer-main #file-content:has(> code-view) {
|
|
404
|
-
padding: 0;
|
|
405
|
-
background: var(--color-box-bg);
|
|
406
|
-
}
|
|
407
369
|
|
|
408
|
-
.file-explorer-main #file-content:has(> pre) pre,
|
|
409
|
-
.
|
|
370
|
+
.nav {
|
|
410
|
-
|
|
371
|
+
display: flex;
|
|
411
|
-
|
|
372
|
+
align-items: center;
|
|
373
|
+
gap: 0.25rem;
|
|
412
|
-
|
|
374
|
+
flex-shrink: 0;
|
|
375
|
+
|
|
376
|
+
div:not(:has(a)) {
|
|
377
|
+
display: none;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
a {
|
|
381
|
+
font-size: 0.9rem;
|
|
382
|
+
font-weight: 500;
|
|
383
|
+
padding: 0.3rem 0.1rem;
|
|
384
|
+
margin-right: 0.75rem;
|
|
385
|
+
cursor: pointer;
|
|
386
|
+
color: var(--color-text);
|
|
387
|
+
border-bottom: 2px solid transparent;
|
|
388
|
+
|
|
389
|
+
&:hover {
|
|
390
|
+
color: var(--color-link);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
&[aria-current="true"] {
|
|
394
|
+
color: var(--color-link);
|
|
395
|
+
border-bottom-color: var(--color-link);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
413
399
|
}
|
|
414
400
|
|
|
415
|
-
/* Blame table */
|
|
401
|
+
/* Blame table — repos/file-blame.njk. */
|
|
416
402
|
table.blame {
|
|
417
403
|
width: 100%;
|
|
418
404
|
border-collapse: collapse;
|
|
419
|
-
font-size: 0.
|
|
405
|
+
font-size: 0.9rem;
|
|
420
406
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
421
407
|
background: var(--color-pre-bg);
|
|
422
408
|
border-radius: 4px;
|
|
423
409
|
overflow: hidden;
|
|
424
|
-
}
|
|
425
410
|
|
|
426
|
-
|
|
411
|
+
td {
|
|
427
|
-
|
|
412
|
+
vertical-align: top;
|
|
428
|
-
|
|
413
|
+
padding: 0.1rem 0.5rem;
|
|
429
|
-
|
|
414
|
+
border-bottom: 1px solid var(--color-box-bg);
|
|
430
|
-
}
|
|
415
|
+
}
|
|
431
416
|
|
|
432
|
-
|
|
417
|
+
.blame-meta {
|
|
433
|
-
|
|
418
|
+
white-space: nowrap;
|
|
434
|
-
|
|
419
|
+
color: var(--color-link);
|
|
435
|
-
|
|
420
|
+
width: 1%;
|
|
436
|
-
}
|
|
421
|
+
}
|
|
437
422
|
|
|
438
|
-
|
|
423
|
+
.blame-line-no {
|
|
439
|
-
|
|
424
|
+
white-space: nowrap;
|
|
440
|
-
|
|
425
|
+
opacity: 0.5;
|
|
441
|
-
|
|
426
|
+
text-align: right;
|
|
442
|
-
|
|
427
|
+
width: 1%;
|
|
443
|
-
}
|
|
428
|
+
}
|
|
444
429
|
|
|
445
|
-
|
|
430
|
+
.blame-content pre {
|
|
446
|
-
|
|
431
|
+
margin: 0;
|
|
447
|
-
|
|
432
|
+
white-space: pre-wrap;
|
|
433
|
+
}
|
|
448
434
|
}
|
|
449
435
|
|
|
450
436
|
/* diff2html ships its own light/dark token sets as plain CSS custom
|
|
@@ -452,7 +438,9 @@ table.blame .blame-content pre {
|
|
|
452
438
|
applies the dark set under a `.d2h-dark-color-scheme` class we never
|
|
453
439
|
add. Remapping the base tokens it actually reads to their `--d2h-dark-*`
|
|
454
440
|
counterparts under our own [data-theme="dark"] gets the same result
|
|
455
|
-
without needing that class or a second copy of every diff2html rule.
|
|
441
|
+
without needing that class or a second copy of every diff2html rule.
|
|
442
|
+
This re-themes a third-party library's own classes rather than styling
|
|
443
|
+
one of our own, so it's left flat/global rather than nested. */
|
|
456
444
|
:root[data-theme="dark"] {
|
|
457
445
|
--d2h-bg-color: var(--d2h-dark-bg-color);
|
|
458
446
|
--d2h-border-color: var(--d2h-dark-border-color);
|
assets/css/shared.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
2
2
|
html { -webkit-text-size-adjust: 100%; }
|
|
3
|
-
body { line-height: 1.5;
|
|
3
|
+
body { line-height: 1.5; }
|
|
4
4
|
img, picture, video, canvas, svg { display: block; max-width: 100%; }
|
|
5
5
|
input, button, textarea, select { font: inherit; }
|
|
6
6
|
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
|
|
@@ -17,7 +17,10 @@ table { border-collapse: collapse; }
|
|
|
17
17
|
browser handles it directly off `@view-transition { navigation: auto }`
|
|
18
18
|
above — this block just replaces the default cross-fade with a slightly
|
|
19
19
|
nicer slide+fade, and keeps header/footer stable instead of fading with
|
|
20
|
-
the rest of the page.
|
|
20
|
+
the rest of the page. The named ::view-transition-group(...) pair below
|
|
21
|
+
operates on the browser's top-layer snapshot tree, not the regular DOM,
|
|
22
|
+
so unlike the view-transition-name declarations themselves (nested
|
|
23
|
+
inside header/footer below) it can't be scoped and has to stay global. */
|
|
21
24
|
::view-transition-old(root),
|
|
22
25
|
::view-transition-new(root) {
|
|
23
26
|
animation-duration: 220ms;
|
|
@@ -48,17 +51,6 @@ table { border-collapse: collapse; }
|
|
|
48
51
|
from { transform: translateY(8px); }
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
/* view-transition-name needs an element with a real box, and site-header/
|
|
52
|
-
site-footer are `display: contents` (no box of their own) — so this
|
|
53
|
-
targets the actual <header>/<footer> element nested inside them. */
|
|
54
|
-
site-header header {
|
|
55
|
-
view-transition-name: site-header;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
site-footer footer {
|
|
59
|
-
view-transition-name: site-footer;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
54
|
::view-transition-group(site-header),
|
|
63
55
|
::view-transition-group(site-footer) {
|
|
64
56
|
animation: none;
|
|
@@ -81,7 +73,7 @@ site-footer footer {
|
|
|
81
73
|
--color-link: light-dark(hsl(211, 100%, 45%), hsl(211, 100%, 60%));
|
|
82
74
|
--color-post-link: light-dark(hsl(0, 0%, 0%), hsl(211, 100%, 60%));
|
|
83
75
|
--color-highlight-bg: light-dark(hsl(183, 80%, 88%), hsl(183, 80%, 18%));
|
|
84
|
-
--color-code-fg: light-dark(hsl(348, 86%, 43%), hsl(348, 86%,
|
|
76
|
+
--color-code-fg: light-dark(hsl(348, 86%, 43%), hsl(348, 86%, 80%));
|
|
85
77
|
--color-code-bg: light-dark(hsl(0, 0%, 95%), hsl(0, 0%, 0%));
|
|
86
78
|
--color-pre-bg: light-dark(hsl(0, 0%, 95%), hsl(0, 0%, 0%));
|
|
87
79
|
--color-tag: light-dark(hsl(152, 96%, 35%), hsl(152, 96%, 44%));
|
|
@@ -119,6 +111,16 @@ site-footer footer {
|
|
|
119
111
|
}
|
|
120
112
|
}
|
|
121
113
|
|
|
114
|
+
:root[data-theme="dark"] {
|
|
115
|
+
--btn-light: block;
|
|
116
|
+
--btn-dark: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
:root[data-theme="light"] {
|
|
120
|
+
--btn-light: none;
|
|
121
|
+
--btn-dark: block;
|
|
122
|
+
}
|
|
123
|
+
|
|
122
124
|
[data-tooltip] {
|
|
123
125
|
position: relative;
|
|
124
126
|
cursor: help;
|
|
@@ -172,135 +174,156 @@ main {
|
|
|
172
174
|
padding-bottom: 1em;
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
site-header,
|
|
176
|
-
site-footer,
|
|
177
|
-
theme-switcher,
|
|
178
|
-
repo-layout,
|
|
179
|
-
file-layout,
|
|
180
|
-
markdown-view,
|
|
181
|
-
|
|
177
|
+
.wrapper {
|
|
178
|
+
flex: 1;
|
|
179
|
+
width: 100%;
|
|
180
|
+
max-width: 960px;
|
|
181
|
+
margin-right: auto;
|
|
182
|
-
|
|
182
|
+
margin-left: auto;
|
|
183
|
+
padding-left: 1em;
|
|
184
|
+
padding-right: 1em;
|
|
183
185
|
}
|
|
184
186
|
|
|
187
|
+
/* site-header.njk/site-footer.njk are plain includes (no wrapping custom
|
|
188
|
+
element), so these are just nested under the real <header>/<footer>
|
|
189
|
+
tag — plain CSS nesting scopes .logo/.links etc to it same as any other
|
|
190
|
+
descendant selector would, no @scope needed since there's only ever
|
|
191
|
+
one header and one footer on the page. */
|
|
185
192
|
header {
|
|
186
193
|
background: #131618;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.logo {
|
|
190
|
-
display: flex;
|
|
191
|
-
font-size: 22px;
|
|
192
|
-
line-height: 56px;
|
|
193
|
-
font-weight: 400;
|
|
194
|
-
margin-bottom: 0;
|
|
195
|
-
float: left;
|
|
196
|
-
color: var(--color-accent);
|
|
197
|
-
margin-right: 8px;
|
|
198
|
-
|
|
199
|
-
&.active,
|
|
200
|
-
&:hover {
|
|
201
|
-
color: var(--color-accent);
|
|
202
|
-
|
|
194
|
+
view-transition-name: site-header;
|
|
203
|
-
text-decoration-color: var(--color-accent);
|
|
204
|
-
text-underline-offset: 8px;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.links {
|
|
209
|
-
display: flex;
|
|
210
|
-
flex-direction: row;
|
|
211
|
-
float: right;
|
|
212
|
-
font-size: 1.4rem;
|
|
213
|
-
line-height: 56px;
|
|
214
|
-
color: white;
|
|
215
195
|
|
|
216
|
-
|
|
196
|
+
.logo {
|
|
217
197
|
display: flex;
|
|
218
|
-
align-items: center;
|
|
219
|
-
font-size: 1.3rem;
|
|
220
|
-
|
|
221
|
-
button {
|
|
222
|
-
padding-right: 12px;
|
|
223
|
-
cursor: pointer;
|
|
224
|
-
margin-bottom: -2px;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
a {
|
|
229
|
-
display: inline-block;
|
|
230
|
-
text-decoration: none;
|
|
231
198
|
font-size: 22px;
|
|
232
199
|
line-height: 56px;
|
|
233
|
-
color: white;
|
|
234
200
|
font-weight: 400;
|
|
235
|
-
|
|
201
|
+
margin-bottom: 0;
|
|
236
|
-
|
|
202
|
+
float: left;
|
|
237
|
-
|
|
238
|
-
|
|
203
|
+
color: var(--color-accent);
|
|
239
|
-
|
|
204
|
+
margin-right: 8px;
|
|
240
|
-
}
|
|
241
205
|
|
|
242
206
|
&.active,
|
|
243
207
|
&:hover {
|
|
244
|
-
color: var(--color-
|
|
208
|
+
color: var(--color-accent);
|
|
245
209
|
text-decoration: underline;
|
|
246
210
|
text-decoration-color: var(--color-accent);
|
|
247
211
|
text-underline-offset: 8px;
|
|
248
212
|
}
|
|
249
213
|
}
|
|
214
|
+
|
|
215
|
+
.links {
|
|
216
|
+
display: flex;
|
|
217
|
+
flex-direction: row;
|
|
218
|
+
float: right;
|
|
219
|
+
font-size: 1.4rem;
|
|
220
|
+
line-height: 56px;
|
|
221
|
+
color: white;
|
|
222
|
+
|
|
223
|
+
#theme-switch {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
font-size: 1.3rem;
|
|
227
|
+
|
|
228
|
+
button {
|
|
229
|
+
background: none;
|
|
230
|
+
border: none;
|
|
231
|
+
padding: 0;
|
|
232
|
+
padding-right: 12px;
|
|
233
|
+
margin-bottom: -2px;
|
|
234
|
+
color: inherit;
|
|
235
|
+
cursor: pointer;
|
|
236
|
+
|
|
237
|
+
&:hover,
|
|
238
|
+
&:focus,
|
|
239
|
+
&:active {
|
|
240
|
+
background: none;
|
|
241
|
+
border: none;
|
|
242
|
+
color: inherit;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
a {
|
|
248
|
+
display: inline-block;
|
|
249
|
+
text-decoration: none;
|
|
250
|
+
font-size: 22px;
|
|
251
|
+
line-height: 56px;
|
|
252
|
+
color: white;
|
|
253
|
+
font-weight: 400;
|
|
254
|
+
padding-left: 12px;
|
|
255
|
+
padding-right: 12px;
|
|
256
|
+
|
|
257
|
+
&:last-child {
|
|
258
|
+
padding-right: 0px;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
&.active,
|
|
262
|
+
&:hover {
|
|
263
|
+
color: var(--color-gray-200);
|
|
264
|
+
text-decoration: underline;
|
|
265
|
+
text-decoration-color: var(--color-accent);
|
|
266
|
+
text-underline-offset: 8px;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* Still a real (the only) Elena/custom element left on the site — it has
|
|
273
|
+
no box of its own; the real box is the <button> it renders. */
|
|
274
|
+
theme-switcher {
|
|
275
|
+
display: contents;
|
|
250
276
|
}
|
|
251
277
|
|
|
252
278
|
footer {
|
|
253
279
|
background: #131618;
|
|
254
|
-
|
|
280
|
+
view-transition-name: site-footer;
|
|
255
281
|
|
|
256
|
-
.container {
|
|
282
|
+
.container {
|
|
257
|
-
|
|
283
|
+
display: flex;
|
|
258
|
-
|
|
284
|
+
flex-direction: row;
|
|
259
|
-
|
|
285
|
+
flex: 1;
|
|
260
|
-
|
|
286
|
+
padding: 16px 0px;
|
|
261
|
-
}
|
|
287
|
+
}
|
|
262
288
|
|
|
263
|
-
.spacer {
|
|
289
|
+
.spacer {
|
|
264
|
-
|
|
290
|
+
flex: 1;
|
|
265
|
-
}
|
|
291
|
+
}
|
|
266
292
|
|
|
267
|
-
.copyright-container {
|
|
293
|
+
.copyright-container {
|
|
268
|
-
|
|
294
|
+
display: flex;
|
|
269
|
-
|
|
295
|
+
align-items: center;
|
|
270
|
-
|
|
296
|
+
justify-content: center;
|
|
271
|
-
|
|
297
|
+
margin-left: 0.5rem;
|
|
272
|
-
|
|
298
|
+
margin-right: 0.5rem;
|
|
273
|
-
|
|
299
|
+
color: var(--color-white);
|
|
274
|
-
|
|
300
|
+
font-size: 1rem;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.link {
|
|
278
|
-
font-size: 1rem;
|
|
279
|
-
line-height: 1.5rem;
|
|
280
|
-
margin-left: 0.5rem;
|
|
281
|
-
color: var(--color-white);
|
|
282
|
-
font-weight: 500;
|
|
283
|
-
text-underline-offset: 2px;
|
|
284
|
-
text-decoration-thickness: 2px;
|
|
285
|
-
text-decoration-line: underline;
|
|
286
|
-
text-decoration-color: white;
|
|
287
|
-
|
|
288
|
-
&:hover {
|
|
289
|
-
color: var(--color-gray-200);
|
|
290
|
-
text-decoration-color: var(--color-gray-200);
|
|
291
301
|
}
|
|
292
|
-
}
|
|
293
302
|
|
|
303
|
+
.link {
|
|
304
|
+
font-size: 1rem;
|
|
305
|
+
line-height: 1.5rem;
|
|
306
|
+
margin-left: 0.5rem;
|
|
294
|
-
:
|
|
307
|
+
color: var(--color-white);
|
|
295
|
-
|
|
308
|
+
font-weight: 500;
|
|
309
|
+
text-underline-offset: 2px;
|
|
310
|
+
text-decoration-thickness: 2px;
|
|
296
|
-
|
|
311
|
+
text-decoration-line: underline;
|
|
297
|
-
|
|
312
|
+
text-decoration-color: white;
|
|
298
313
|
|
|
314
|
+
&:hover {
|
|
315
|
+
color: var(--color-gray-200);
|
|
299
|
-
|
|
316
|
+
text-decoration-color: var(--color-gray-200);
|
|
300
|
-
|
|
317
|
+
}
|
|
301
|
-
|
|
318
|
+
}
|
|
302
319
|
}
|
|
303
320
|
|
|
321
|
+
/* Syntax highlighting theme: shared by code-view's own file-viewer pre and
|
|
322
|
+
by fenced code blocks inside markdown-view's article content (both
|
|
323
|
+
Prism-highlighted blog-post code and plain markdown-it README code
|
|
324
|
+
fences). It's genuinely cross-cutting rather than owned by one
|
|
325
|
+
component — scoping it to either would mean duplicating the whole token
|
|
326
|
+
palette in the other — so it stays global. */
|
|
304
327
|
pre[class*="language-"],
|
|
305
328
|
code[class*="language-"] {
|
|
306
329
|
background: var(--color-pre-bg);
|
|
@@ -314,10 +337,21 @@ pre.code-view,
|
|
|
314
337
|
margin: 1rem 0;
|
|
315
338
|
overflow-x: auto;
|
|
316
339
|
border-radius: 4px;
|
|
317
|
-
font-size: 0.8rem;
|
|
318
340
|
line-height: 1.5;
|
|
319
341
|
}
|
|
320
342
|
|
|
343
|
+
/* File-viewer code (pre.code-view) matches the rest of that layout's
|
|
344
|
+
0.9rem scale (see repos.css); blog/README fenced code blocks stay at
|
|
345
|
+
0.8rem — they're a different context and weren't asked to change. */
|
|
346
|
+
pre.code-view {
|
|
347
|
+
font-size: 0.9rem;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
pre[class*="language-"],
|
|
351
|
+
.markdown-body pre {
|
|
352
|
+
font-size: 0.8rem;
|
|
353
|
+
}
|
|
354
|
+
|
|
321
355
|
pre.code-view,
|
|
322
356
|
.markdown-body pre {
|
|
323
357
|
background: var(--color-pre-bg);
|
|
@@ -379,18 +413,3 @@ code[class*="language-"] {
|
|
|
379
413
|
.token.property {
|
|
380
414
|
color: var(--token-attr-name);
|
|
381
415
|
}
|
|
382
|
-
|
|
383
|
-
.active-tab {
|
|
384
|
-
text-underline-offset: 4px;
|
|
385
|
-
text-decoration: underline;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
.wrapper {
|
|
389
|
-
flex: 1;
|
|
390
|
-
width: 100%;
|
|
391
|
-
max-width: 960px;
|
|
392
|
-
margin-right: auto;
|
|
393
|
-
margin-left: auto;
|
|
394
|
-
padding-left: 1em;
|
|
395
|
-
padding-right: 1em;
|
|
396
|
-
}
|
assets/css/workers/cv.css
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/* Nested under .cv-page — this ships in the site-wide bundle.css, and a
|
|
2
|
+
bare h1/.pdf selector would otherwise leak onto every other page's
|
|
3
|
+
h1/.pdf, the same mistake posts-index.css made before it got dropped
|
|
4
|
+
from the merge entirely (see eleventy.config.js's CSS_FILES_TO_MERGE
|
|
5
|
+
comment). */
|
|
6
|
+
.cv-page {
|
|
1
7
|
h1 {
|
|
2
8
|
display: flex;
|
|
3
9
|
align-items: flex-start;
|
|
@@ -5,7 +11,7 @@
|
|
|
5
11
|
font-size: 2rem;
|
|
6
12
|
margin-bottom: 1rem;
|
|
7
13
|
|
|
8
|
-
a {
|
|
14
|
+
a {
|
|
9
15
|
margin-left: 0.4rem;
|
|
10
16
|
margin-top: 0.8rem;
|
|
11
17
|
color: var(--color-link);
|
|
@@ -14,4 +20,5 @@ a {
|
|
|
14
20
|
|
|
15
21
|
.pdf {
|
|
16
22
|
border: 1px solid black;
|
|
17
|
-
}
|
|
23
|
+
}
|
|
24
|
+
}
|
assets/css/workers/home.css
CHANGED
|
@@ -1,218 +1,223 @@
|
|
|
1
|
-
.description {
|
|
2
|
-
|
|
1
|
+
/* Homepage-only styling, nested under .pageContainer (the page's own
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
top-level wrapper — see src/index.njk) now that this file ships in the
|
|
5
|
-
font-size: 1rem;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
h2 {
|
|
10
|
-
font-size: 1.4rem;
|
|
11
|
-
font-weight: 600;
|
|
12
|
-
|
|
13
|
-
@media (max-width: 720px) {
|
|
14
|
-
font-size: 1.15rem;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
section {
|
|
19
|
-
display: flex;
|
|
20
|
-
flex-direction: column;
|
|
21
|
-
margin-top: 1.5rem;
|
|
22
|
-
margin-bottom: 1rem;
|
|
23
|
-
|
|
24
|
-
@media (max-width: 720px) {
|
|
25
|
-
margin-top: 1.5rem;
|
|
26
|
-
margin-bottom: 0rem;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.rounded-md {
|
|
31
|
-
border-radius: 0.375rem;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.repos {
|
|
35
|
-
display: grid;
|
|
36
|
-
|
|
3
|
+
site-wide bundle.css instead of being the only stylesheet loaded on
|
|
37
|
-
gap: 1rem;
|
|
38
|
-
margin-top: 0.5rem;
|
|
39
|
-
|
|
40
|
-
|
|
4
|
+
this page. Without this nesting, the bare `h2`/`section` selectors
|
|
41
|
-
gap: 1rem;
|
|
42
|
-
|
|
5
|
+
below would apply to every h2/section on every other page too. */
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
6
|
.pageContainer {
|
|
47
7
|
margin-left: auto;
|
|
48
8
|
margin-right: auto;
|
|
49
9
|
margin-bottom: 2.5rem;
|
|
50
|
-
}
|
|
51
10
|
|
|
52
|
-
.
|
|
11
|
+
.description {
|
|
53
|
-
|
|
12
|
+
font-size: 1.1rem;
|
|
54
|
-
line-height: 2.25rem;
|
|
55
|
-
font-weight: 700;
|
|
56
|
-
margin-bottom: 1rem;
|
|
57
13
|
|
|
58
|
-
|
|
14
|
+
@media (max-width: 720px) {
|
|
59
|
-
|
|
15
|
+
font-size: 1rem;
|
|
16
|
+
}
|
|
60
17
|
}
|
|
61
|
-
}
|
|
62
18
|
|
|
63
|
-
|
|
19
|
+
h2 {
|
|
64
|
-
display: flex;
|
|
65
|
-
|
|
20
|
+
font-size: 1.4rem;
|
|
66
|
-
|
|
21
|
+
font-weight: 600;
|
|
67
22
|
|
|
68
|
-
.firstPara {
|
|
69
|
-
|
|
23
|
+
@media (max-width: 720px) {
|
|
70
|
-
|
|
24
|
+
font-size: 1.15rem;
|
|
71
|
-
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
72
27
|
|
|
28
|
+
section {
|
|
73
|
-
|
|
29
|
+
display: flex;
|
|
74
|
-
|
|
30
|
+
flex-direction: column;
|
|
75
|
-
|
|
31
|
+
margin-top: 1.5rem;
|
|
32
|
+
margin-bottom: 1rem;
|
|
76
33
|
|
|
77
|
-
|
|
34
|
+
@media (max-width: 720px) {
|
|
78
|
-
display: grid;
|
|
79
|
-
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
80
|
-
|
|
35
|
+
margin-top: 1.5rem;
|
|
36
|
+
margin-bottom: 0rem;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
81
39
|
|
|
40
|
+
.rounded-md {
|
|
82
|
-
|
|
41
|
+
border-radius: 0.375rem;
|
|
83
|
-
gap: 0rem;
|
|
84
42
|
}
|
|
85
43
|
|
|
44
|
+
.repos {
|
|
86
|
-
|
|
45
|
+
display: grid;
|
|
87
|
-
grid-template-columns:
|
|
46
|
+
grid-template-columns: auto auto;
|
|
88
|
-
gap:
|
|
47
|
+
gap: 1rem;
|
|
48
|
+
margin-top: 0.5rem;
|
|
49
|
+
|
|
50
|
+
@media (max-width: 720px) {
|
|
51
|
+
gap: 1rem;
|
|
52
|
+
grid-template-columns: auto;
|
|
53
|
+
}
|
|
89
54
|
}
|
|
90
|
-
}
|
|
91
55
|
|
|
92
|
-
.
|
|
56
|
+
.title {
|
|
93
|
-
text-overflow: ellipsis;
|
|
94
|
-
overflow: hidden;
|
|
95
|
-
padding: 0.5rem;
|
|
96
|
-
background: var(--color-box-bg);
|
|
97
|
-
color: var(--color-text);
|
|
98
|
-
|
|
57
|
+
font-size: 1.875rem;
|
|
58
|
+
line-height: 2.25rem;
|
|
59
|
+
font-weight: 700;
|
|
60
|
+
margin-bottom: 1rem;
|
|
99
61
|
|
|
100
|
-
a {
|
|
101
|
-
|
|
62
|
+
@media (max-width: 720px) {
|
|
63
|
+
font-size: 1.6rem;
|
|
64
|
+
}
|
|
102
65
|
}
|
|
103
66
|
|
|
104
|
-
h4 {
|
|
105
|
-
font-size: 1.05rem;
|
|
106
|
-
|
|
67
|
+
.firstSection {
|
|
107
|
-
|
|
68
|
+
display: flex;
|
|
108
|
-
border-bottom: 1px solid #495057;
|
|
109
|
-
|
|
69
|
+
flex-direction: column;
|
|
110
|
-
white-space: nowrap;
|
|
111
|
-
overflow: hidden;
|
|
112
|
-
margin-bottom: 0;
|
|
113
|
-
padding: 0;
|
|
114
70
|
}
|
|
115
71
|
|
|
116
|
-
.
|
|
72
|
+
.firstPara {
|
|
117
|
-
margin-top: 0.5rem;
|
|
118
73
|
display: flex;
|
|
119
|
-
flex-
|
|
74
|
+
flex-direction: row;
|
|
120
|
-
gap: 0.25rem;
|
|
121
75
|
}
|
|
122
76
|
|
|
123
|
-
.tag {
|
|
124
|
-
|
|
77
|
+
.linkUnderline {
|
|
125
|
-
padding: 0.15rem 0.5rem 0.5rem 0rem;
|
|
126
|
-
|
|
78
|
+
text-decoration-line: underline;
|
|
127
|
-
font-size: 0.95rem;
|
|
128
|
-
font-weight: 500;
|
|
129
79
|
}
|
|
130
|
-
}
|
|
131
80
|
|
|
132
|
-
.
|
|
81
|
+
.gridContainer {
|
|
133
|
-
ul {
|
|
134
82
|
display: grid;
|
|
83
|
+
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
84
|
+
gap: 1rem;
|
|
85
|
+
|
|
86
|
+
@media (max-width: 720px) {
|
|
135
|
-
|
|
87
|
+
gap: 0rem;
|
|
136
|
-
grid-template-columns: auto auto auto;
|
|
137
|
-
text-align: center;
|
|
138
|
-
margin-top: 0.5rem;
|
|
139
|
-
|
|
88
|
+
}
|
|
140
89
|
|
|
141
|
-
|
|
90
|
+
@media (min-width: 640px) {
|
|
142
|
-
|
|
91
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
143
|
-
|
|
92
|
+
gap: 2rem;
|
|
144
93
|
}
|
|
145
94
|
}
|
|
146
|
-
}
|
|
147
95
|
|
|
148
|
-
.
|
|
96
|
+
.repo {
|
|
149
|
-
|
|
97
|
+
text-overflow: ellipsis;
|
|
150
|
-
|
|
98
|
+
overflow: hidden;
|
|
151
|
-
gap: 0.5rem;
|
|
152
|
-
grid-template-columns: auto;
|
|
153
|
-
|
|
99
|
+
padding: 0.5rem;
|
|
100
|
+
background: var(--color-box-bg);
|
|
101
|
+
color: var(--color-text);
|
|
102
|
+
font-size: 0.95rem;
|
|
154
103
|
|
|
155
|
-
|
|
104
|
+
a {
|
|
156
|
-
display: flex;
|
|
157
|
-
flex-direction: row;
|
|
158
|
-
align-items: baseline;
|
|
159
|
-
padding: 0.25rem;
|
|
160
|
-
|
|
105
|
+
color: var(--color-link);
|
|
161
|
-
|
|
106
|
+
}
|
|
162
107
|
|
|
163
|
-
|
|
108
|
+
h4 {
|
|
164
|
-
|
|
109
|
+
font-size: 1.05rem;
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
line-height: 1.2;
|
|
112
|
+
border-bottom: 1px solid #495057;
|
|
165
|
-
|
|
113
|
+
text-overflow: ellipsis;
|
|
114
|
+
white-space: nowrap;
|
|
115
|
+
overflow: hidden;
|
|
116
|
+
margin-bottom: 0;
|
|
166
|
-
|
|
117
|
+
padding: 0;
|
|
167
118
|
}
|
|
168
119
|
|
|
169
|
-
|
|
120
|
+
.tags {
|
|
170
|
-
|
|
121
|
+
margin-top: 0.5rem;
|
|
122
|
+
display: flex;
|
|
123
|
+
flex-wrap: wrap;
|
|
124
|
+
gap: 0.25rem;
|
|
171
125
|
}
|
|
172
126
|
|
|
173
|
-
|
|
127
|
+
.tag {
|
|
128
|
+
color: var(--color-tag);
|
|
129
|
+
padding: 0.15rem 0.5rem 0.5rem 0rem;
|
|
130
|
+
border-radius: 0.25rem;
|
|
131
|
+
font-size: 0.95rem;
|
|
174
132
|
font-weight: 500;
|
|
175
|
-
margin-left: 0.25rem;
|
|
176
|
-
margin-right: 0.5rem;
|
|
177
133
|
}
|
|
178
134
|
}
|
|
179
|
-
}
|
|
180
135
|
|
|
181
|
-
.
|
|
136
|
+
.interests {
|
|
182
|
-
|
|
137
|
+
ul {
|
|
183
|
-
|
|
138
|
+
display: grid;
|
|
139
|
+
gap: 0.5rem;
|
|
140
|
+
grid-template-columns: auto auto auto;
|
|
184
|
-
|
|
141
|
+
text-align: center;
|
|
142
|
+
margin-top: 0.5rem;
|
|
143
|
+
font-size: 1.1rem;
|
|
144
|
+
|
|
145
|
+
li {
|
|
146
|
+
background-color: var(--color-box-bg);
|
|
147
|
+
padding: 0.25rem;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
185
150
|
}
|
|
186
151
|
|
|
152
|
+
.contact {
|
|
187
|
-
|
|
153
|
+
ul {
|
|
188
|
-
|
|
154
|
+
display: grid;
|
|
189
|
-
|
|
155
|
+
gap: 0.5rem;
|
|
190
|
-
|
|
156
|
+
grid-template-columns: auto;
|
|
191
|
-
text-align: center;
|
|
192
|
-
|
|
157
|
+
margin-top: 0.5rem;
|
|
193
158
|
|
|
159
|
+
li {
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: row;
|
|
162
|
+
align-items: baseline;
|
|
163
|
+
padding: 0.25rem;
|
|
164
|
+
background-color: var(--color-box-bg);
|
|
165
|
+
font-size: 1.1rem;
|
|
166
|
+
|
|
194
|
-
|
|
167
|
+
@media (max-width: 720px) {
|
|
168
|
+
font-size: 1rem;
|
|
195
|
-
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
a {
|
|
174
|
+
color: var(--color-link);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
strong {
|
|
178
|
+
font-weight: 500;
|
|
179
|
+
margin-left: 0.25rem;
|
|
180
|
+
margin-right: 0.5rem;
|
|
181
|
+
}
|
|
196
182
|
}
|
|
183
|
+
}
|
|
197
184
|
|
|
185
|
+
.tools {
|
|
198
|
-
|
|
186
|
+
img {
|
|
199
|
-
|
|
187
|
+
width: 36px;
|
|
200
|
-
|
|
188
|
+
height: 36px;
|
|
201
|
-
background-color: var(--color-box-bg);
|
|
202
|
-
padding: 0.25rem;
|
|
203
|
-
|
|
189
|
+
}
|
|
204
190
|
|
|
205
|
-
|
|
191
|
+
ul {
|
|
192
|
+
display: grid;
|
|
193
|
+
gap: 0.5rem;
|
|
194
|
+
grid-template-columns: auto auto auto auto;
|
|
195
|
+
text-align: center;
|
|
196
|
+
margin-top: 0.5rem;
|
|
197
|
+
|
|
198
|
+
@media (max-width: 720px) {
|
|
199
|
+
grid-template-columns: auto auto auto;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
li {
|
|
206
203
|
display: flex;
|
|
207
|
-
flex: 1;
|
|
208
|
-
flex-direction: row;
|
|
209
204
|
align-items: center;
|
|
210
|
-
text-align: left;
|
|
211
|
-
|
|
205
|
+
background-color: var(--color-box-bg);
|
|
212
|
-
|
|
206
|
+
padding: 0.25rem;
|
|
207
|
+
font-size: 1rem;
|
|
213
208
|
|
|
214
|
-
|
|
209
|
+
a {
|
|
210
|
+
display: flex;
|
|
215
211
|
flex: 1;
|
|
212
|
+
flex-direction: row;
|
|
213
|
+
align-items: center;
|
|
214
|
+
text-align: left;
|
|
215
|
+
margin-left: 0.25rem;
|
|
216
|
+
margin-right: 0.25rem;
|
|
217
|
+
|
|
218
|
+
div {
|
|
219
|
+
flex: 1;
|
|
220
|
+
}
|
|
216
221
|
}
|
|
217
222
|
}
|
|
218
223
|
}
|
eleventy.config.js
CHANGED
|
@@ -1,23 +1,54 @@
|
|
|
1
|
+
import { readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import syntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight";
|
|
2
4
|
import pluginRss from "@11ty/eleventy-plugin-rss";
|
|
3
5
|
import { register, ssr } from "@elenajs/ssr";
|
|
4
|
-
import CurrentYear from "./src/components/current-year.js";
|
|
5
6
|
import ThemeSwitcher from "./src/components/theme-switcher.js";
|
|
6
|
-
import SiteHeader from "./src/components/site-header.js";
|
|
7
|
-
import SiteFooter from "./src/components/site-footer.js";
|
|
8
|
-
import RepoLayout from "./src/components/repo-layout.js";
|
|
9
|
-
import FileLayout from "./src/components/file-layout.js";
|
|
10
|
-
import MarkdownView from "./src/components/markdown-view.js";
|
|
11
|
-
import CodeView from "./src/components/code-view.js";
|
|
12
7
|
import { fileIcon, folderIcon } from "./src/_helpers/fileIcons.js";
|
|
13
8
|
|
|
14
|
-
// @elenajs/ssr's expansion pass didn't recursively re-walk a component's
|
|
15
|
-
//
|
|
9
|
+
// theme-switcher is the only Elena component left — everything else
|
|
10
|
+
// (site-header, site-footer, repo-layout, file-layout, markdown-view,
|
|
11
|
+
// code-view, the cv page) was SSR-only markup-with-props, which plain
|
|
12
|
+
// Nunjucks includes/macros already do natively (see src/_includes/
|
|
16
|
-
// site-header
|
|
13
|
+
// site-header.njk, repo-layout.njk, file-layout.njk). Keeping those as
|
|
14
|
+
// Elena components meant carrying the whole elena-ssr transform, the
|
|
17
|
-
//
|
|
15
|
+
// patched fork (patches/@elenajs+ssr+*.patch), @scope workarounds for
|
|
18
|
-
//
|
|
16
|
+
// having no shadow root, and a custom <slot> patch for the one case
|
|
17
|
+
// (the page layout) that needed to pass through arbitrary children —
|
|
18
|
+
// none of which pulls its weight once htmx is already doing the actual
|
|
19
|
+
// dynamic navigation. theme-switcher earns its keep because it's the one
|
|
20
|
+
// piece that's genuinely isomorphic: the same source SSRs the initial
|
|
21
|
+
// button and, loaded again client-side, drives the real click handler.
|
|
22
|
+
register(ThemeSwitcher);
|
|
23
|
+
|
|
24
|
+
// Every page used to pick and choose its own subset of these via `css`/
|
|
25
|
+
// `extraCss` frontmatter (e.g. repo pages loading repos.css + markdown.css
|
|
26
|
+
// + diff2html.min.css, the homepage loading just home.css). Now that
|
|
27
|
+
// everything below is @scope'd (see shared.css/repos.css/markdown.css/
|
|
28
|
+
// workers/home.css), there's no naming-collision reason to keep them
|
|
29
|
+
// apart, so they're concatenated into one cacheable stylesheet every page
|
|
30
|
+
// loads — fewer render-blocking requests than 2-4 separate <link>s.
|
|
31
|
+
//
|
|
19
|
-
//
|
|
32
|
+
// issues-detail.css/issues-index.css/posts-index.css/only-bible-app*.css
|
|
33
|
+
// are deliberately left out — none of them are unscoped-safe. The
|
|
34
|
+
// issues-* pair is leftover from a page that never made it into this
|
|
20
|
-
|
|
35
|
+
// rewrite. The other three are fully superseded by inline styles on their
|
|
36
|
+
// own pages now (posts.njk, only-bible-app/*.njk have zero matching
|
|
37
|
+
// classes left) — merging them site-wide used to be harmless because
|
|
38
|
+
// each was the only stylesheet on its own page, but their unscoped bare-
|
|
39
|
+
// tag selectors (a plain `a {...}`, `h1 {...}`, etc., not `.some-class
|
|
40
|
+
// a`) would otherwise leak onto every other page's matching tags once
|
|
41
|
+
// bundled together, which is exactly what broke tools/interests links on
|
|
42
|
+
// the homepage — inherited from posts-index.css's `ul li a` rule matching
|
|
43
|
+
// any list of links, not just the post list it was meant for.
|
|
44
|
+
const CSS_FILES_TO_MERGE = [
|
|
45
|
+
"assets/css/shared.css",
|
|
46
|
+
"assets/css/repos.css",
|
|
47
|
+
"assets/css/markdown.css",
|
|
48
|
+
"assets/css/diff2html.min.css",
|
|
49
|
+
"assets/css/workers/home.css",
|
|
50
|
+
"assets/css/workers/cv.css",
|
|
51
|
+
];
|
|
21
52
|
|
|
22
53
|
export default function (eleventyConfig) {
|
|
23
54
|
eleventyConfig.addPassthroughCopy({ assets: "assets" });
|
|
@@ -51,7 +82,6 @@ export default function (eleventyConfig) {
|
|
|
51
82
|
eleventyConfig.addFilter("readableDate", (value) =>
|
|
52
83
|
new Date(value).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }),
|
|
53
84
|
);
|
|
54
|
-
eleventyConfig.addFilter("json", (value) => JSON.stringify(value));
|
|
55
85
|
eleventyConfig.addFilter("fileIcon", fileIcon);
|
|
56
86
|
eleventyConfig.addFilter("folderIcon", folderIcon);
|
|
57
87
|
// A folder should start open only when the active file lives inside it —
|
|
@@ -68,19 +98,6 @@ export default function (eleventyConfig) {
|
|
|
68
98
|
const value = bytes / 1024 ** i;
|
|
69
99
|
return `${i === 0 ? value : value.toFixed(1)}${units[i]}`;
|
|
70
100
|
});
|
|
71
|
-
// Forces HTML-escaping on a value regardless of whether Eleventy/Nunjucks
|
|
72
|
-
// already marked it "safe" upstream (e.g. rendered markdown/post content)
|
|
73
|
-
// — needed to pass real HTML through a custom element attribute so it
|
|
74
|
-
// round-trips correctly instead of breaking the tag it's embedded in.
|
|
75
|
-
eleventyConfig.addFilter("attrEscape", (value) =>
|
|
76
|
-
String(value)
|
|
77
|
-
.replace(/&/g, "&")
|
|
78
|
-
.replace(/</g, "<")
|
|
79
|
-
.replace(/>/g, ">")
|
|
80
|
-
.replace(/"/g, """)
|
|
81
|
-
.replace(/'/g, "'"),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
101
|
eleventyConfig.addTransform("elena-ssr", (content, outputPath) => {
|
|
85
102
|
if (outputPath && outputPath.endsWith(".html")) {
|
|
86
103
|
return ssr(content);
|
|
@@ -88,6 +105,17 @@ export default function (eleventyConfig) {
|
|
|
88
105
|
return content;
|
|
89
106
|
});
|
|
90
107
|
|
|
108
|
+
// Runs after passthrough copy has placed every file in CSS_FILES_TO_MERGE
|
|
109
|
+
// under dist/, so it reads the already-copied output rather than the
|
|
110
|
+
// assets/ source tree (that's also where diff2html.min.css lands, copied
|
|
111
|
+
// from node_modules above — there's no separate "source" copy of it).
|
|
112
|
+
eleventyConfig.on("eleventy.after", ({ dir }) => {
|
|
113
|
+
const outputDir = dir.output;
|
|
114
|
+
const merged = CSS_FILES_TO_MERGE.map((file) => readFileSync(path.join(outputDir, file), "utf8")).join("\n");
|
|
115
|
+
writeFileSync(path.join(outputDir, "assets/css/bundle.css"), merged);
|
|
116
|
+
for (const file of CSS_FILES_TO_MERGE) rmSync(path.join(outputDir, file));
|
|
117
|
+
});
|
|
118
|
+
|
|
91
119
|
return {
|
|
92
120
|
markdownTemplateEngine: false,
|
|
93
121
|
dir: {
|
patches/@elenajs+ssr+1.0.0-alpha.11.patch
CHANGED
|
@@ -57,10 +57,38 @@ index 2253f72..8351dc2 100644
|
|
|
57
57
|
+ return normalized.replace(RESTORE_PATTERN, (_, i) => protectedChunks[Number(i)]);
|
|
58
58
|
}
|
|
59
59
|
diff --git a/node_modules/@elenajs/ssr/src/index.js b/node_modules/@elenajs/ssr/src/index.js
|
|
60
|
-
index a134eda..
|
|
60
|
+
index a134eda..b2080a8 100644
|
|
61
61
|
--- a/node_modules/@elenajs/ssr/src/index.js
|
|
62
62
|
+++ b/node_modules/@elenajs/ssr/src/index.js
|
|
63
|
+
@@ -297,6 +297,9 @@ function serializeAttrs(attrs) {
|
|
64
|
+
const WS = /[\t\n\r ]+/g;
|
|
65
|
+
const ALL_WS = /[^\t\n\r ]/;
|
|
66
|
+
|
|
67
|
+
+/** Matches a default <slot> marker: <slot></slot>, <slot/>, or <slot />. */
|
|
68
|
+
+const SLOT_PATTERN = /<slot(?:\s*\/)?\s*>(?:\s*<\/slot>)?/;
|
|
69
|
+
+
|
|
70
|
+
/**
|
|
71
|
+
* Check whether a component class has a meaningful render() method,
|
|
72
|
+
* including inherited ones. Walks the prototype chain and distinguishes
|
|
63
|
-
@@ -
|
|
73
|
+
@@ -395,10 +398,34 @@ function walk(nodes, preserveWhitespace = false) {
|
|
74
|
+
// Mark as hydrated so CSS targeting :scope:not([hydrated]) doesn’t
|
|
75
|
+
// double-style the host alongside the already-rendered inner element.
|
|
76
|
+
attrs.hydrated = "";
|
|
77
|
+
+ // A component that wants its light-DOM children projected in place
|
|
78
|
+
+ // (rather than only available flattened as this._text) writes a
|
|
79
|
+
+ // literal <slot></slot> marker in its own render() output. Only
|
|
80
|
+
+ // walk the original children (expanding any components inside
|
|
81
|
+
+ // them too) when a component actually asked for it — most
|
|
82
|
+
+ // components don't take children at all, so this stays free for
|
|
83
|
+
+ // them. This is a plain string substitution done once, before the
|
|
84
|
+
+ // re-parse below, rather than a real DOM-based <slot> element —
|
|
85
|
+
+ // there's no shadow root here to make that meaningful, and it
|
|
86
|
+
+ // avoids the escape/attribute/reparse round trip a "pass content
|
|
87
|
+
+ // as a prop" approach would need for arbitrary structured HTML.
|
|
88
|
+
+ if (SLOT_PATTERN.test(innerHTML)) {
|
|
89
|
+
+ const slotted = walk(children, pre);
|
|
90
|
+
+ innerHTML = innerHTML.replace(SLOT_PATTERN, slotted);
|
|
91
|
+
+ }
|
|
64
92
|
}
|
|
65
93
|
}
|
|
66
94
|
|
src/_data/localRepos.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import MarkdownIt from "markdown-it";
|
|
2
2
|
import { discoverLocalRepos } from "../_helpers/gitRepos.js";
|
|
3
|
+
import { highlightFenceCode } from "../_helpers/highlightCode.js";
|
|
3
4
|
|
|
4
|
-
const md = new MarkdownIt({ html: true, linkify: true });
|
|
5
|
+
const md = new MarkdownIt({ html: true, linkify: true, highlight: highlightFenceCode });
|
|
5
6
|
|
|
6
7
|
export default async () => {
|
|
7
8
|
const repos = discoverLocalRepos();
|
src/_helpers/highlightCode.js
CHANGED
|
@@ -29,23 +29,37 @@ const EXT_TO_LANG = {
|
|
|
29
29
|
typ: null,
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
// Fenced-code-block language identifiers (the "rust" in ```rust) mostly
|
|
33
|
+
// already match Prism's own component ids, unlike file extensions —
|
|
32
|
-
|
|
34
|
+
// except for a handful of common short forms people actually type in
|
|
33
|
-
|
|
35
|
+
// READMEs, which need the same kind of aliasing EXT_TO_LANG does above.
|
|
34
|
-
const
|
|
36
|
+
const FENCE_LANG_ALIASES = {
|
|
37
|
+
js: "javascript",
|
|
38
|
+
sh: "bash",
|
|
39
|
+
rs: "rust",
|
|
40
|
+
ts: "typescript",
|
|
41
|
+
yml: "yaml",
|
|
42
|
+
html: "markup",
|
|
35
|
-
|
|
43
|
+
htm: "markup",
|
|
44
|
+
};
|
|
36
45
|
|
|
37
|
-
const lang = Object.prototype.hasOwnProperty.call(EXT_TO_LANG, ext) ? EXT_TO_LANG[ext] : ext;
|
|
38
|
-
|
|
46
|
+
const grammarCache = new Map();
|
|
39
|
-
if (lang) {
|
|
40
|
-
if (!Prism.languages[lang]) loadPrismLanguage(lang);
|
|
41
|
-
grammar = Prism.languages[lang] ?? null;
|
|
42
|
-
}
|
|
43
47
|
|
|
48
|
+
/** Load (and cache) a Prism grammar by its canonical component id. */
|
|
49
|
+
const loadGrammar = (lang) => {
|
|
50
|
+
if (grammarCache.has(lang)) return grammarCache.get(lang);
|
|
51
|
+
if (!Prism.languages[lang]) loadPrismLanguage(lang);
|
|
52
|
+
const grammar = Prism.languages[lang] ?? null;
|
|
44
53
|
const result = grammar ? { lang, grammar } : null;
|
|
45
|
-
|
|
54
|
+
grammarCache.set(lang, result);
|
|
46
55
|
return result;
|
|
47
56
|
};
|
|
48
57
|
|
|
58
|
+
const getLanguage = (ext) => {
|
|
59
|
+
const lang = Object.prototype.hasOwnProperty.call(EXT_TO_LANG, ext) ? EXT_TO_LANG[ext] : ext;
|
|
60
|
+
return lang ? loadGrammar(lang) : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
49
63
|
/**
|
|
50
64
|
* Highlight source text for a given file extension using Prism, at build
|
|
51
65
|
* time — no client-side highlighter ships to the browser. Falls back to
|
|
@@ -62,5 +76,22 @@ export const highlightCode = (text, ext) => {
|
|
|
62
76
|
return { html: Prism.highlight(text, found.grammar, found.lang), lang: found.lang };
|
|
63
77
|
};
|
|
64
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Highlight a fenced markdown code block by its declared language (the
|
|
81
|
+
* "rust" in ```rust), for markdown-it's `highlight` option — used for
|
|
82
|
+
* repo READMEs, which render through a separate MarkdownIt instance
|
|
83
|
+
* rather than highlightCode()'s extension-keyed lookup. markdown-it
|
|
84
|
+
* wraps whatever this returns in <pre><code class="language-{lang}">,
|
|
85
|
+
* matching the shared pre/code styling and .token.* colors in
|
|
86
|
+
* shared.css. Falls back to plain escaped text, same as highlightCode.
|
|
87
|
+
*
|
|
88
|
+
* @returns {string} inner HTML for markdown-it's fence renderer
|
|
89
|
+
*/
|
|
90
|
+
export const highlightFenceCode = (text, lang) => {
|
|
91
|
+
const normalized = lang ? (FENCE_LANG_ALIASES[lang.toLowerCase()] ?? lang.toLowerCase()) : null;
|
|
92
|
+
const found = normalized ? loadGrammar(normalized) : null;
|
|
93
|
+
return found ? Prism.highlight(text, found.grammar, found.lang) : escapeHtml(text);
|
|
94
|
+
};
|
|
95
|
+
|
|
65
96
|
const escapeHtml = (str) =>
|
|
66
97
|
String(str).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
src/_includes/file-layout.njk
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% macro fileLayout(repo, filepath, filename, ext, tab) %}
|
|
2
|
+
{% set base = "/repos/" + repo + "/files/" + filepath %}
|
|
3
|
+
<div class="file-layout">
|
|
4
|
+
<div class="title">{{ filename | fileIcon(ext) | safe }}<span>{{ filename }}</span></div>
|
|
5
|
+
<div class="nav">
|
|
6
|
+
<div><a href="{{ base }}/" hx-get="{{ base }}/" hx-select="#file-pane" hx-target="#file-pane" hx-swap="outerHTML" hx-push-url="true" aria-current="{% if tab == 'Contents' %}true{% else %}false{% endif %}">Contents</a></div>
|
|
7
|
+
<div>|</div>
|
|
8
|
+
<div><a href="{{ base }}/history/" hx-get="{{ base }}/history/" hx-select="#file-pane" hx-target="#file-pane" hx-swap="outerHTML" hx-push-url="true" aria-current="{% if tab == 'History' %}true{% else %}false{% endif %}">History</a></div>
|
|
9
|
+
<div>|</div>
|
|
10
|
+
<div><a href="{{ base }}/blame/" hx-get="{{ base }}/blame/" hx-select="#file-pane" hx-target="#file-pane" hx-swap="outerHTML" hx-push-url="true" aria-current="{% if tab == 'Blame' %}true{% else %}false{% endif %}">Blame</a></div>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
{% endmacro %}
|
src/_includes/layout.njk
CHANGED
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
<link rel="icon" type="image/svg+xml" href="/assets/icons/icon.svg" />
|
|
14
14
|
<link rel="alternate" type="application/rss+xml" title="{{ title or site.title }}" href="/rss.xml" />
|
|
15
15
|
<link rel="canonical" href="{{ site.url }}{{ page.url }}" />
|
|
16
|
-
<link rel="stylesheet" href="/assets/css/
|
|
16
|
+
<link rel="stylesheet" href="/assets/css/bundle.css" />
|
|
17
|
-
{% if css %}<link rel="stylesheet" href="{{ css }}" />{% endif %}
|
|
18
|
-
{% for sheet in extraCss %}<link rel="stylesheet" href="{{ sheet }}" />{% endfor %}
|
|
19
17
|
<title>{{ title or site.title }}</title>
|
|
20
18
|
<meta name="title" content="{{ title or site.title }}" />
|
|
21
19
|
<meta name="description" content="{{ description or site.description }}" />
|
|
@@ -30,10 +28,10 @@
|
|
|
30
28
|
<script src="/assets/vendor/htmx.min.js"></script>
|
|
31
29
|
</head>
|
|
32
30
|
<body>
|
|
33
|
-
|
|
31
|
+
{% include "site-header.njk" %}
|
|
34
32
|
<div class="wrapper">
|
|
35
33
|
<main>{{ content | safe }}</main>
|
|
36
34
|
</div>
|
|
37
|
-
|
|
35
|
+
{% include "site-footer.njk" %}
|
|
38
36
|
</body>
|
|
39
37
|
</html>
|
src/_includes/post.njk
CHANGED
|
@@ -6,5 +6,5 @@ layout: layout.njk
|
|
|
6
6
|
<h2 style="font-size:1.1rem;font-weight:500">{{ description }}</h2>
|
|
7
7
|
<time style="font-size:1.1rem;color:light-dark(hsl(from var(--color-text) h s 40%), hsl(from var(--color-text) h s 80%))">{{ pubDate | readableDate }}</time>
|
|
8
8
|
<hr style="margin:0.5rem 0;border-color:var(--color-text)" />
|
|
9
|
-
<markdown-
|
|
9
|
+
<article class="markdown-body">{{ content | safe }}</article>
|
|
10
10
|
</article>
|
src/_includes/repo-layout.njk
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{% macro repoLayout(repo, title, desc, tags, tab) %}
|
|
2
|
+
<div class="repo">
|
|
3
|
+
<div class="header">
|
|
4
|
+
<h1><a href="/">~repos</a>/{{ title }}</h1>
|
|
5
|
+
<div class="tags">{% for tag in tags %}<span class="tag">#{{ tag }}</span>{% endfor %}</div>
|
|
6
|
+
</div>
|
|
7
|
+
<h3>GIT_CONFIG_PARAMETERS="'http.version=HTTP/1.1'" git clone <a>https://git.pyrossh.dev/{{ title }}/.git</a> {{ title }}</h3>
|
|
8
|
+
<div class="headerExtension"><h2>{{ desc }}</h2></div>
|
|
9
|
+
<hr />
|
|
10
|
+
<div class="nav">
|
|
11
|
+
<div><a href="/repos/{{ repo }}/" hx-get="/repos/{{ repo }}/" hx-select="#repo-page" hx-target="#repo-page" hx-swap="outerHTML" hx-push-url="true" class="{% if tab == 'Readme' %}active-tab{% endif %}">Readme</a><span>|</span></div>
|
|
12
|
+
<div><a href="/repos/{{ repo }}/commits/" hx-get="/repos/{{ repo }}/commits/" hx-select="#repo-page" hx-target="#repo-page" hx-swap="outerHTML" hx-push-url="true" class="{% if tab == 'Commits' %}active-tab{% endif %}">Commits</a><span>|</span></div>
|
|
13
|
+
<div><a href="/repos/{{ repo }}/files/" hx-get="/repos/{{ repo }}/files/" hx-select="#repo-page" hx-target="#repo-page" hx-swap="outerHTML" hx-push-url="true" class="{% if tab == 'Files' %}active-tab{% endif %}">Files</a><span>|</span></div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
{% endmacro %}
|
src/_includes/site-footer.njk
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<footer>
|
|
2
|
+
<div class="wrapper">
|
|
3
|
+
<div class="container">
|
|
4
|
+
<div class="spacer"></div>
|
|
5
|
+
<p class="copyright-container">
|
|
6
|
+
Copyright © {{ site.year }}
|
|
7
|
+
<a class="link" href="{{ site.url }}">{{ site.title }}</a>
|
|
8
|
+
</p>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</footer>
|
src/_includes/site-header.njk
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<div class="wrapper">
|
|
3
|
+
<nav>
|
|
4
|
+
<a href="/" class="logo{% if page.url == '/' %} active{% endif %}">木 {{ site.title }}</a>
|
|
5
|
+
<div class="links">
|
|
6
|
+
<span id="theme-switch"><theme-switcher></theme-switcher></span>
|
|
7
|
+
<div>|</div>
|
|
8
|
+
{% for item in site.navItems %}
|
|
9
|
+
<a href="{{ item.href }}" class="{% if page.url == item.href or (item.href != '/' and page.url.startsWith(item.href)) %}active{% endif %}">{{ item.label }}</a><div>|</div>
|
|
10
|
+
{% endfor %}
|
|
11
|
+
</div>
|
|
12
|
+
</nav>
|
|
13
|
+
</div>
|
|
14
|
+
</header>
|
src/components/code-view.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Elena, html, unsafeHTML } from "@elenajs/core";
|
|
2
|
-
|
|
3
|
-
// `body` is pre-highlighted HTML from Prism (see highlightCode.js), built
|
|
4
|
-
// at build time — no highlighter ships to the client. Unlike a raw-text
|
|
5
|
-
// prop this needs unsafeHTML() since it's real markup (<span
|
|
6
|
-
// class="token...">), not text to be re-escaped for display.
|
|
7
|
-
export default class CodeView extends Elena(HTMLElement) {
|
|
8
|
-
static tagName = "code-view";
|
|
9
|
-
static props = ["lang", "body"];
|
|
10
|
-
lang = "";
|
|
11
|
-
body = "";
|
|
12
|
-
|
|
13
|
-
render() {
|
|
14
|
-
return html`<pre class="code-view" data-lang="${this.lang}"><code class="language-${this.lang}">${unsafeHTML(this.body)}</code></pre>`;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
CodeView.define();
|
src/components/current-year.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Elena, html } from "@elenajs/core";
|
|
2
|
-
|
|
3
|
-
export default class CurrentYear extends Elena(HTMLElement) {
|
|
4
|
-
static tagName = "current-year";
|
|
5
|
-
|
|
6
|
-
render() {
|
|
7
|
-
return html`${new Date().getFullYear()}`;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
CurrentYear.define();
|
src/components/file-layout.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Elena, html, unsafeHTML } from "@elenajs/core";
|
|
2
|
-
import { fileIcon } from "../_helpers/fileIcons.js";
|
|
3
|
-
|
|
4
|
-
// Chrome-only, same reasoning as repo-layout.js: file content is a
|
|
5
|
-
// sibling in the page template, not a slotted child of this element.
|
|
6
|
-
// Tab links swap #file-pane (this element + #file-content together) so
|
|
7
|
-
// this element's own aria-current state updates on htmx navigation too —
|
|
8
|
-
// swapping only #file-content would leave the tab underline stuck.
|
|
9
|
-
export default class FileLayout extends Elena(HTMLElement) {
|
|
10
|
-
static tagName = "file-layout";
|
|
11
|
-
static props = ["repo", "filepath", "filename", "tab"];
|
|
12
|
-
repo = "";
|
|
13
|
-
filepath = "";
|
|
14
|
-
filename = "";
|
|
15
|
-
tab = "Contents";
|
|
16
|
-
|
|
17
|
-
render() {
|
|
18
|
-
const base = `/repos/${this.repo}/files/${this.filepath}`;
|
|
19
|
-
const ext = this.filename.includes(".") ? this.filename.split(".").pop().toLowerCase() : "";
|
|
20
|
-
const tab = (label, href) => html`<div><a
|
|
21
|
-
href="${href}"
|
|
22
|
-
hx-get="${href}"
|
|
23
|
-
hx-select="#file-pane"
|
|
24
|
-
hx-target="#file-pane"
|
|
25
|
-
hx-swap="outerHTML"
|
|
26
|
-
hx-push-url="true"
|
|
27
|
-
aria-current="${this.tab === label ? "true" : "false"}"
|
|
28
|
-
>${label}</a></div>`;
|
|
29
|
-
return html`<div class="file-layout">
|
|
30
|
-
<div class="title">${unsafeHTML(fileIcon(this.filename, ext))}<span>${this.filename}</span></div>
|
|
31
|
-
<div class="nav">
|
|
32
|
-
${tab("Contents", `${base}/`)}
|
|
33
|
-
<div>|</div>
|
|
34
|
-
${tab("History", `${base}/history/`)}
|
|
35
|
-
<div>|</div>
|
|
36
|
-
${tab("Blame", `${base}/blame/`)}
|
|
37
|
-
</div>
|
|
38
|
-
</div>`;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
FileLayout.define();
|
src/components/markdown-view.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Elena, html, unsafeHTML } from "@elenajs/core";
|
|
2
|
-
|
|
3
|
-
// Content comes in via the `body` prop (already-rendered, trusted HTML —
|
|
4
|
-
// see the `attrEscape` Nunjucks filter), not light-DOM children: this
|
|
5
|
-
// version of @elenajs/ssr doesn't project light-DOM into <slot> during the
|
|
6
|
-
// server render pass, it just drops it, so anything wrapping real content
|
|
7
|
-
// has to pass it as a prop and inject it with unsafeHTML.
|
|
8
|
-
export default class MarkdownView extends Elena(HTMLElement) {
|
|
9
|
-
static tagName = "markdown-view";
|
|
10
|
-
static props = ["body"];
|
|
11
|
-
body = "";
|
|
12
|
-
|
|
13
|
-
render() {
|
|
14
|
-
return html`<article class="markdown-body">${unsafeHTML(this.body)}</article>`;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
MarkdownView.define();
|
src/components/repo-layout.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Elena, html } from "@elenajs/core";
|
|
2
|
-
|
|
3
|
-
// Chrome-only: renders the repo header/tab nav from props and nothing
|
|
4
|
-
// else. The actual tab content (readme/files/commits) is rendered as a
|
|
5
|
-
// plain sibling in the page template, not as a child of this element —
|
|
6
|
-
// see markdown-view.js for why light-DOM children aren't used here.
|
|
7
|
-
//
|
|
8
|
-
// The tab links swap #repo-page (this element + the content sibling
|
|
9
|
-
// together, see the *.njk templates), not just the content — otherwise
|
|
10
|
-
// this element itself never re-renders on an htmx navigation, so its own
|
|
11
|
-
// active-tab underline would stay stuck on whichever tab the page
|
|
12
|
-
// originally loaded on.
|
|
13
|
-
export default class RepoLayout extends Elena(HTMLElement) {
|
|
14
|
-
static tagName = "repo-layout";
|
|
15
|
-
static props = ["repo", "title", "desc", "tags", "tab"];
|
|
16
|
-
repo = "";
|
|
17
|
-
title = "";
|
|
18
|
-
desc = "";
|
|
19
|
-
tags = [];
|
|
20
|
-
tab = "Readme";
|
|
21
|
-
|
|
22
|
-
render() {
|
|
23
|
-
const tabHref = (t) => (t === "Readme" ? `/repos/${this.repo}/` : `/repos/${this.repo}/${t.toLowerCase()}/`);
|
|
24
|
-
return html`<div class="repo">
|
|
25
|
-
<div class="header">
|
|
26
|
-
<h1><a href="/">~repos</a>/${this.title}</h1>
|
|
27
|
-
<div class="tags">${this.tags.map((tag) => html`<span class="tag">#${tag}</span>`)}</div>
|
|
28
|
-
</div>
|
|
29
|
-
<h3>GIT_CONFIG_PARAMETERS="'http.version=HTTP/1.1'" git clone <a>https://git.pyrossh.dev/${this.title}/.git</a> ${this.title}</h3>
|
|
30
|
-
<div class="headerExtension"><h2>${this.desc}</h2></div>
|
|
31
|
-
<hr />
|
|
32
|
-
<div class="nav">
|
|
33
|
-
${["Readme", "Commits", "Files"].map((t) => {
|
|
34
|
-
const href = tabHref(t);
|
|
35
|
-
return html`<div><a
|
|
36
|
-
href="${href}"
|
|
37
|
-
hx-get="${href}"
|
|
38
|
-
hx-select="#repo-page"
|
|
39
|
-
hx-target="#repo-page"
|
|
40
|
-
hx-swap="outerHTML"
|
|
41
|
-
hx-push-url="true"
|
|
42
|
-
class="${this.tab === t ? "active-tab" : ""}"
|
|
43
|
-
>${t}</a><span>|</span></div>`;
|
|
44
|
-
})}
|
|
45
|
-
</div>
|
|
46
|
-
</div>`;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
RepoLayout.define();
|
src/components/site-footer.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Elena, html } from "@elenajs/core";
|
|
2
|
-
import site from "../_data/site.js";
|
|
3
|
-
|
|
4
|
-
export default class SiteFooter extends Elena(HTMLElement) {
|
|
5
|
-
static tagName = "site-footer";
|
|
6
|
-
|
|
7
|
-
render() {
|
|
8
|
-
return html`<footer>
|
|
9
|
-
<div class="wrapper">
|
|
10
|
-
<div class="container">
|
|
11
|
-
<div class="spacer"></div>
|
|
12
|
-
<p class="copyright-container">
|
|
13
|
-
Copyright © <current-year></current-year>
|
|
14
|
-
<a class="link" href="${site.url}">${site.title}</a>
|
|
15
|
-
</p>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
</footer>`;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
SiteFooter.define();
|
src/components/site-header.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Elena, html } from "@elenajs/core";
|
|
2
|
-
import site from "../_data/site.js";
|
|
3
|
-
|
|
4
|
-
export default class SiteHeader extends Elena(HTMLElement) {
|
|
5
|
-
static tagName = "site-header";
|
|
6
|
-
static props = ["path"];
|
|
7
|
-
path = "/";
|
|
8
|
-
|
|
9
|
-
render() {
|
|
10
|
-
const isActive = (href) => this.path === href || (href !== "/" && this.path?.startsWith(href));
|
|
11
|
-
return html`<header>
|
|
12
|
-
<div class="wrapper">
|
|
13
|
-
<nav>
|
|
14
|
-
<a href="/" class="logo${this.path === "/" ? " active" : ""}">木 ${site.title}</a>
|
|
15
|
-
<div class="links">
|
|
16
|
-
<span id="theme-switch"><theme-switcher></theme-switcher></span>
|
|
17
|
-
<div>|</div>
|
|
18
|
-
${site.navItems.map(
|
|
19
|
-
(item) =>
|
|
20
|
-
html`<a href="${item.href}" class="${isActive(item.href) ? "active" : ""}">${item.label}</a><div>|</div>`,
|
|
21
|
-
)}
|
|
22
|
-
</div>
|
|
23
|
-
</nav>
|
|
24
|
-
</div>
|
|
25
|
-
</header>`;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
SiteHeader.define();
|
src/cv.njk
CHANGED
|
@@ -3,14 +3,13 @@ layout: layout.njk
|
|
|
3
3
|
title: Curriculum Vitae
|
|
4
4
|
description: Peter John CV
|
|
5
5
|
permalink: /cv/
|
|
6
|
-
css: /assets/css/workers/cv.css
|
|
7
6
|
---
|
|
7
|
+
<div class="cv-page">
|
|
8
|
-
<
|
|
8
|
+
<h1>
|
|
9
|
-
<h1 style="display:flex;align-items:flex-start;font-weight:800;font-size:2rem;margin-bottom:1rem">
|
|
10
9
|
Curriculum Vitae
|
|
11
|
-
<a href="/assets/pdfs/resume.pdf" target="_blank" rel="noopener noreferrer"
|
|
10
|
+
<a href="/assets/pdfs/resume.pdf" target="_blank" rel="noopener noreferrer">↗</a>
|
|
12
11
|
</h1>
|
|
13
|
-
<div
|
|
12
|
+
<div class="pdf">
|
|
14
|
-
<img src="/assets/pdfs/resume.svg" alt="Resume"
|
|
13
|
+
<img src="/assets/pdfs/resume.svg" alt="Resume" />
|
|
15
14
|
</div>
|
|
16
15
|
</div>
|
src/index.njk
CHANGED
|
@@ -3,7 +3,6 @@ layout: layout.njk
|
|
|
3
3
|
title: pyrossh
|
|
4
4
|
description: "Tech Lead from Bangalore who likes to create frameworks and programming languages."
|
|
5
5
|
permalink: /
|
|
6
|
-
css: /assets/css/workers/home.css
|
|
7
6
|
---
|
|
8
7
|
<div class="pageContainer">
|
|
9
8
|
<h1 class="title">Hello!</h1>
|
src/only-bible-app/index.njk
CHANGED
|
@@ -3,7 +3,6 @@ layout: layout.njk
|
|
|
3
3
|
title: Only Bible App
|
|
4
4
|
description: The only bible app you will ever need
|
|
5
5
|
permalink: /only-bible-app/
|
|
6
|
-
css: /assets/css/workers/only-bible-app.css
|
|
7
6
|
---
|
|
8
7
|
<div style="display:flex;flex:1;flex-direction:column;padding-bottom:16rem">
|
|
9
8
|
<div style="display:flex;flex-direction:column;align-items:center">
|
src/only-bible-app/privacy-policy.njk
CHANGED
|
@@ -3,7 +3,6 @@ layout: layout.njk
|
|
|
3
3
|
title: Privacy Policy | Only Bible App
|
|
4
4
|
description: Privacy Policy | Only Bible App
|
|
5
5
|
permalink: /only-bible-app/privacy-policy/
|
|
6
|
-
css: /assets/css/workers/only-bible-app-privacy.css
|
|
7
6
|
---
|
|
8
7
|
<h1 style="font-size:1.875rem;line-height:2.25rem;font-weight:700">Privacy Policy</h1>
|
|
9
8
|
<p>
|
src/only-bible-app/terms-and-conditions.njk
CHANGED
|
@@ -3,7 +3,6 @@ layout: layout.njk
|
|
|
3
3
|
title: Terms and Conditions | Only Bible App
|
|
4
4
|
description: Terms and Conditions | Only Bible App
|
|
5
5
|
permalink: /only-bible-app/terms-and-conditions/
|
|
6
|
-
css: /assets/css/workers/only-bible-app-terms.css
|
|
7
6
|
---
|
|
8
7
|
<div style="padding-bottom:2.5rem;margin-top:2.5rem">
|
|
9
8
|
<h1 style="font-weight:700;font-size:1.875rem;line-height:2.25rem">Terms and Conditions</h1>
|
src/posts.njk
CHANGED
|
@@ -3,7 +3,6 @@ layout: layout.njk
|
|
|
3
3
|
title: Posts
|
|
4
4
|
description: Posts
|
|
5
5
|
permalink: /posts/
|
|
6
|
-
css: /assets/css/workers/posts-index.css
|
|
7
6
|
---
|
|
8
7
|
<h1 style="font-weight:800;font-size:2rem">Posts</h1>
|
|
9
8
|
<ul style="display:flex;flex-direction:column;list-style:none;padding:0">
|
src/repos/commit.njk
CHANGED
|
@@ -5,15 +5,12 @@ pagination:
|
|
|
5
5
|
alias: item
|
|
6
6
|
permalink: "/repos/{{ item.repoId }}/commits/{{ item.commit.hash }}/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "{{ item.commit.shortHash }} | {{ item.repo.data.title }}"
|
|
14
10
|
---
|
|
11
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
15
12
|
<div id="repo-page">
|
|
16
|
-
|
|
13
|
+
{{ repoLayout(item.repo.id, item.repo.data.title, item.repo.data.description, item.repo.data.tags, "Commits") }}
|
|
17
14
|
<div id="repo-content">
|
|
18
15
|
<div class="event">
|
|
19
16
|
<div>
|
src/repos/commits.njk
CHANGED
|
@@ -5,15 +5,12 @@ pagination:
|
|
|
5
5
|
alias: repo
|
|
6
6
|
permalink: "/repos/{{ repo.id }}/commits/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "Commits | {{ repo.data.title }}"
|
|
14
10
|
---
|
|
11
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
15
12
|
<div id="repo-page">
|
|
16
|
-
|
|
13
|
+
{{ repoLayout(repo.id, repo.data.title, repo.data.description, repo.data.tags, "Commits") }}
|
|
17
14
|
<div id="repo-content">
|
|
18
15
|
{% if repo.commits.length %}
|
|
19
16
|
{% for commit in repo.commits %}
|
src/repos/file-blame.njk
CHANGED
|
@@ -5,15 +5,13 @@ pagination:
|
|
|
5
5
|
alias: item
|
|
6
6
|
permalink: "/repos/{{ item.repoId }}/files/{{ item.file.path }}/blame/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "{{ item.file.name }} blame | {{ item.repo.data.title }}"
|
|
14
10
|
---
|
|
11
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
12
|
+
{% from "file-layout.njk" import fileLayout %}
|
|
15
13
|
<div id="repo-page">
|
|
16
|
-
|
|
14
|
+
{{ repoLayout(item.repo.id, item.repo.data.title, item.repo.data.description, item.repo.data.tags, "Files") }}
|
|
17
15
|
<div id="repo-content">
|
|
18
16
|
<div class="file-explorer">
|
|
19
17
|
<aside class="file-explorer-tree">
|
|
@@ -21,7 +19,7 @@ eleventyComputed:
|
|
|
21
19
|
{{ renderNodes(item.repo.tree, item.repoId, item.file.path) }}
|
|
22
20
|
</aside>
|
|
23
21
|
<div id="file-pane" class="file-explorer-main">
|
|
24
|
-
|
|
22
|
+
{{ fileLayout(item.repoId, item.file.path, item.file.name, item.file.ext, "Blame") }}
|
|
25
23
|
<div id="file-content">
|
|
26
24
|
{% if item.blame.length %}
|
|
27
25
|
<table class="blame">
|
src/repos/file-history.njk
CHANGED
|
@@ -5,15 +5,13 @@ pagination:
|
|
|
5
5
|
alias: item
|
|
6
6
|
permalink: "/repos/{{ item.repoId }}/files/{{ item.file.path }}/history/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "{{ item.file.name }} history | {{ item.repo.data.title }}"
|
|
14
10
|
---
|
|
11
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
12
|
+
{% from "file-layout.njk" import fileLayout %}
|
|
15
13
|
<div id="repo-page">
|
|
16
|
-
|
|
14
|
+
{{ repoLayout(item.repo.id, item.repo.data.title, item.repo.data.description, item.repo.data.tags, "Files") }}
|
|
17
15
|
<div id="repo-content">
|
|
18
16
|
<div class="file-explorer">
|
|
19
17
|
<aside class="file-explorer-tree">
|
|
@@ -21,7 +19,7 @@ eleventyComputed:
|
|
|
21
19
|
{{ renderNodes(item.repo.tree, item.repoId, item.file.path) }}
|
|
22
20
|
</aside>
|
|
23
21
|
<div id="file-pane" class="file-explorer-main">
|
|
24
|
-
|
|
22
|
+
{{ fileLayout(item.repoId, item.file.path, item.file.name, item.file.ext, "History") }}
|
|
25
23
|
<div id="file-content">
|
|
26
24
|
{% if item.history.length %}
|
|
27
25
|
{% for commit in item.history %}
|
src/repos/file.njk
CHANGED
|
@@ -5,15 +5,13 @@ pagination:
|
|
|
5
5
|
alias: item
|
|
6
6
|
permalink: "/repos/{{ item.repoId }}/files/{{ item.file.path }}/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "{{ item.file.name }} | {{ item.repo.data.title }}"
|
|
14
10
|
---
|
|
11
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
12
|
+
{% from "file-layout.njk" import fileLayout %}
|
|
15
13
|
<div id="repo-page">
|
|
16
|
-
|
|
14
|
+
{{ repoLayout(item.repo.id, item.repo.data.title, item.repo.data.description, item.repo.data.tags, "Files") }}
|
|
17
15
|
<div id="repo-content">
|
|
18
16
|
<div class="file-explorer">
|
|
19
17
|
<aside class="file-explorer-tree">
|
|
@@ -21,7 +19,7 @@ eleventyComputed:
|
|
|
21
19
|
{{ renderNodes(item.repo.tree, item.repoId, item.file.path) }}
|
|
22
20
|
</aside>
|
|
23
21
|
<div id="file-pane" class="file-explorer-main">
|
|
24
|
-
|
|
22
|
+
{{ fileLayout(item.repoId, item.file.path, item.file.name, item.file.ext, "Contents") }}
|
|
25
23
|
<div id="file-content">
|
|
26
24
|
{% if item.render.kind == "binary" %}
|
|
27
25
|
<p>Binary file ({{ item.file.size }} bytes).</p>
|
|
@@ -30,7 +28,7 @@ eleventyComputed:
|
|
|
30
28
|
{% elif item.render.kind == "image" %}
|
|
31
29
|
<img src="{{ item.render.src }}" alt="{{ item.file.path }}" />
|
|
32
30
|
{% else %}
|
|
33
|
-
<code-view lang="{{ item.render.lang }}"
|
|
31
|
+
<pre class="code-view" data-lang="{{ item.render.lang }}"><code class="language-{{ item.render.lang }}">{{ item.render.html | safe }}</code></pre>
|
|
34
32
|
{% endif %}
|
|
35
33
|
</div>
|
|
36
34
|
</div>
|
src/repos/files-index.njk
CHANGED
|
@@ -5,15 +5,12 @@ pagination:
|
|
|
5
5
|
alias: repo
|
|
6
6
|
permalink: "/repos/{{ repo.id }}/files/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "Files | {{ repo.data.title }}"
|
|
14
10
|
---
|
|
11
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
15
12
|
<div id="repo-page">
|
|
16
|
-
|
|
13
|
+
{{ repoLayout(repo.id, repo.data.title, repo.data.description, repo.data.tags, "Files") }}
|
|
17
14
|
<div id="repo-content">
|
|
18
15
|
<div class="file-explorer">
|
|
19
16
|
<aside class="file-explorer-tree">
|
src/repos/repo.njk
CHANGED
|
@@ -5,19 +5,16 @@ pagination:
|
|
|
5
5
|
alias: repo
|
|
6
6
|
permalink: "/repos/{{ repo.id }}/"
|
|
7
7
|
layout: layout.njk
|
|
8
|
-
css: /assets/css/repos.css
|
|
9
|
-
extraCss:
|
|
10
|
-
- /assets/css/markdown.css
|
|
11
|
-
- /assets/css/diff2html.min.css
|
|
12
8
|
eleventyComputed:
|
|
13
9
|
title: "{{ repo.data.title }}"
|
|
14
10
|
description: "{{ repo.data.description }}"
|
|
15
11
|
---
|
|
12
|
+
{% from "repo-layout.njk" import repoLayout %}
|
|
16
13
|
<div id="repo-page">
|
|
17
|
-
|
|
14
|
+
{{ repoLayout(repo.id, repo.data.title, repo.data.description, repo.data.tags, "Readme") }}
|
|
18
15
|
<div id="repo-content">
|
|
19
16
|
{% if repo.readmeHtml %}
|
|
20
|
-
<markdown-
|
|
17
|
+
<article class="markdown-body">{{ repo.readmeHtml | safe }}</article>
|
|
21
18
|
{% else %}
|
|
22
19
|
<p>No README found.</p>
|
|
23
20
|
{% endif %}
|