@abreen/tada 1.6.2 → 1.7.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.
- package/build/utils/front-matter.ts +28 -0
- package/build/utils/markdown.ts +41 -12
- package/init/content/index.md +2 -0
- package/init/content/labs/00/VowelCounter.java.md +2 -0
- package/init/content/labs/00/index.md +2 -0
- package/init/content/labs/01/index.md +2 -0
- package/init/content/labs/index.md +2 -0
- package/init/content/lectures/01/Pair.java.md +2 -0
- package/init/content/lectures/01/index.md +2 -0
- package/init/content/lectures/02/index.md +2 -0
- package/init/content/lectures/index.md +2 -0
- package/init/content/markdown.md +16 -4
- package/init/content/problem_sets/index.html +2 -0
- package/package.json +1 -1
- package/src/_alerts.scss +1 -1
- package/src/_base.scss +8 -10
- package/src/_mixins.scss +4 -0
- package/src/header/index.ts +12 -0
- package/src/print/style.scss +24 -22
- package/src/style.scss +26 -3
- package/src/toc/style.scss +4 -3
- package/templates/_theme.scss +1 -0
|
@@ -57,11 +57,39 @@ function parseFrontMatterPlainText(rawContent: string): RawParsedFrontMatter {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function parseFrontMatterStandard(rawContent: string): RawParsedFrontMatter {
|
|
61
|
+
const lines = rawContent.split(/\r?\n/);
|
|
62
|
+
|
|
63
|
+
// Caller has already verified lines[0] === '---'
|
|
64
|
+
for (let i = 1; i < lines.length; i++) {
|
|
65
|
+
if (lines[i].trimEnd() === '---') {
|
|
66
|
+
return {
|
|
67
|
+
frontMatter: lines.slice(1, i).join('\n'),
|
|
68
|
+
content: lines.slice(i + 1).join('\n'),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw new Error(
|
|
74
|
+
'Front matter starts with --- but no closing --- delimiter was found',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
60
78
|
export function parseFrontMatter(
|
|
61
79
|
rawContent: string,
|
|
62
80
|
ext: string,
|
|
63
81
|
): RawParsedFrontMatter {
|
|
64
82
|
if (extensionIsMarkdown(ext) || ext === '.html') {
|
|
83
|
+
// Detect standard YAML front matter (first line is exactly ---,
|
|
84
|
+
// ignoring trailing whitespace and \r)
|
|
85
|
+
const firstNewline = rawContent.indexOf('\n');
|
|
86
|
+
const firstLine =
|
|
87
|
+
firstNewline === -1 ? rawContent : rawContent.slice(0, firstNewline);
|
|
88
|
+
|
|
89
|
+
if (firstLine.trimEnd() === '---') {
|
|
90
|
+
return parseFrontMatterStandard(rawContent);
|
|
91
|
+
}
|
|
92
|
+
|
|
65
93
|
return parseFrontMatterPlainText(rawContent);
|
|
66
94
|
} else {
|
|
67
95
|
// unknown type, return raw
|
package/build/utils/markdown.ts
CHANGED
|
@@ -36,6 +36,22 @@ function capitalize(str: string): string {
|
|
|
36
36
|
return str[0].toUpperCase() + str.slice(1);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
const MAX_FOOTNOTES = 35;
|
|
40
|
+
|
|
41
|
+
export function footnoteLabel(oneBasedIndex: number): string {
|
|
42
|
+
if (oneBasedIndex < 1 || oneBasedIndex > MAX_FOOTNOTES) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Tada supports at most ${MAX_FOOTNOTES} footnotes per page ` +
|
|
45
|
+
`(9 digits + 26 capital letters), but footnote ${oneBasedIndex} ` +
|
|
46
|
+
`was requested. Reduce the number of footnotes on this page.`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (oneBasedIndex <= 9) {
|
|
50
|
+
return String(oneBasedIndex);
|
|
51
|
+
}
|
|
52
|
+
return String.fromCharCode('A'.charCodeAt(0) + (oneBasedIndex - 10));
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
export function createMarkdown(
|
|
40
56
|
siteVariables: SiteVariables,
|
|
41
57
|
options: CreateMarkdownOptions = {},
|
|
@@ -196,24 +212,22 @@ export function createMarkdown(
|
|
|
196
212
|
});
|
|
197
213
|
|
|
198
214
|
/*
|
|
199
|
-
* Customize markdown-it-footnote renderer
|
|
215
|
+
* Customize markdown-it-footnote renderer.
|
|
216
|
+
*
|
|
217
|
+
* Footnotes are labeled with single characters from Inter's ss06
|
|
218
|
+
* stylistic set: 1-9 for the first nine footnotes, then A-Z. The
|
|
219
|
+
* default <ol> numbering is suppressed via CSS and we inject a
|
|
220
|
+
* marker span per item so the label sequence can mix digits and
|
|
221
|
+
* letters.
|
|
200
222
|
*/
|
|
201
223
|
markdown.renderer.rules.footnote_block_open = () =>
|
|
202
224
|
'<div class="footnotes"><p class="title">Footnotes</p><ol>';
|
|
203
225
|
|
|
204
226
|
markdown.renderer.rules.footnote_block_close = () => '</ol></div>';
|
|
205
227
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
tokens: Token[],
|
|
210
|
-
idx: number,
|
|
211
|
-
options: Options,
|
|
212
|
-
env: unknown,
|
|
213
|
-
self: Renderer,
|
|
214
|
-
) => {
|
|
215
|
-
const str = caption(tokens, idx, options, env, self);
|
|
216
|
-
return str.slice(1, str.length - 1);
|
|
228
|
+
markdown.renderer.rules.footnote_caption = (tokens: Token[], idx: number) => {
|
|
229
|
+
const id = tokens[idx].meta!.id as number;
|
|
230
|
+
return footnoteLabel(id + 1);
|
|
217
231
|
};
|
|
218
232
|
|
|
219
233
|
const footnoteRef = markdown.renderer.rules.footnote_ref!;
|
|
@@ -224,11 +238,26 @@ export function createMarkdown(
|
|
|
224
238
|
env: unknown,
|
|
225
239
|
self: Renderer,
|
|
226
240
|
) =>
|
|
241
|
+
// Prepend a non-breaking space so the reference link cannot wrap
|
|
242
|
+
// away from the word it follows.
|
|
243
|
+
'\u00a0' +
|
|
227
244
|
footnoteRef(tokens, idx, options, env, self)
|
|
228
245
|
.replace('<sup class="footnote-ref">', '')
|
|
229
246
|
.replace('</sup>', '')
|
|
230
247
|
.replace('<a href="', '<a class="footnote-ref" href="');
|
|
231
248
|
|
|
249
|
+
markdown.renderer.rules.footnote_open = (tokens: Token[], idx: number) => {
|
|
250
|
+
const id = tokens[idx].meta!.id as number;
|
|
251
|
+
const label = footnoteLabel(id + 1);
|
|
252
|
+
const refid = id + 1;
|
|
253
|
+
return (
|
|
254
|
+
`<li id="fn${refid}" class="footnote-item">` +
|
|
255
|
+
`<span class="footnote-marker" aria-hidden="true">${label}</span> `
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
markdown.renderer.rules.footnote_close = () => '</li>\n';
|
|
260
|
+
|
|
232
261
|
const footnoteAnchor = markdown.renderer.rules.footnote_anchor!;
|
|
233
262
|
markdown.renderer.rules.footnote_anchor = (
|
|
234
263
|
tokens: Token[],
|
package/init/content/index.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
---
|
|
1
2
|
parent: /labs/00/index.html
|
|
2
3
|
parentLabel: Lab 0
|
|
3
4
|
title: Counting vowels
|
|
4
5
|
author: alex
|
|
5
6
|
description: An example of a literate Java program that takes predefined input.
|
|
6
7
|
stdin: "hello, world!\n"
|
|
8
|
+
---
|
|
7
9
|
|
|
8
10
|
A Java program can process input in different ways: it could open a file and
|
|
9
11
|
read it; it could open a network connection and wait for data from another
|
package/init/content/markdown.md
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
+
---
|
|
1
2
|
title: Markdown Examples
|
|
2
3
|
description: Examples of Markdown syntax supported by Tada.
|
|
3
4
|
author: alex
|
|
4
5
|
published: 2026-03-20
|
|
5
6
|
toc: true
|
|
7
|
+
---
|
|
6
8
|
|
|
7
9
|
Markdown and HTML files in the `content/` directory
|
|
8
10
|
must contain ["front matter"][front-matter] (YAML-formatted metadata).
|
|
9
11
|
For example, this file's first five lines look like this:
|
|
10
12
|
|
|
11
13
|
```text
|
|
14
|
+
---
|
|
12
15
|
title: Markdown Examples
|
|
13
|
-
description: Examples of Markdown syntax supported by
|
|
16
|
+
description: Examples of Markdown syntax supported by Tada.
|
|
14
17
|
toc: true
|
|
15
18
|
author: alex
|
|
19
|
+
---
|
|
16
20
|
```
|
|
17
21
|
|
|
18
22
|
This page contains a table of contents because the front matter
|
|
@@ -182,10 +186,10 @@ footnote.[^example-footnote]
|
|
|
182
186
|
Definition lists are supported using the same syntax as Presto:
|
|
183
187
|
|
|
184
188
|
Stack
|
|
185
|
-
: Last-in first-out collection.
|
|
189
|
+
: Last-in first-out collection.[^footnote-2]
|
|
186
190
|
|
|
187
191
|
Queue
|
|
188
|
-
: First-in first-out collection.
|
|
192
|
+
: First-in first-out collection.[^footnote-3]
|
|
189
193
|
|
|
190
194
|
Binary tree
|
|
191
195
|
: Hierarchical structure where each node has up to two children.
|
|
@@ -417,7 +421,15 @@ Here's a time range:
|
|
|
417
421
|
|
|
418
422
|
|
|
419
423
|
[^example-footnote]: Footnotes render inside a numbered list at the bottom of
|
|
420
|
-
the page.
|
|
424
|
+
the page.
|
|
425
|
+
|
|
426
|
+
Here's a second paragraph.
|
|
427
|
+
|
|
428
|
+
Here's a third paragraph.
|
|
429
|
+
|
|
430
|
+
[^footnote-2]: Also known as "LIFO".
|
|
431
|
+
|
|
432
|
+
[^footnote-3]: Also known as "FIFO".
|
|
421
433
|
|
|
422
434
|
[front-matter]: https://www.npmjs.com/package/front-matter
|
|
423
435
|
[markdown-it]: https://markdown-it.github.io/
|
package/package.json
CHANGED
package/src/_alerts.scss
CHANGED
package/src/_base.scss
CHANGED
|
@@ -92,7 +92,7 @@ code {
|
|
|
92
92
|
font-size: var(--mono-font-size);
|
|
93
93
|
background: var(--bg2-color);
|
|
94
94
|
border-radius: var(--mono-border-radius);
|
|
95
|
-
padding:
|
|
95
|
+
padding: var(--mono-padding);
|
|
96
96
|
overflow-wrap: anywhere;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -139,18 +139,16 @@ details .content {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
@media (hover: hover) {
|
|
142
|
-
details summary {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
@include link-underline;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
&:active {
|
|
149
|
-
color: var(--link-active-color);
|
|
150
|
-
}
|
|
142
|
+
details summary:hover {
|
|
143
|
+
color: var(--fg2-color);
|
|
144
|
+
@include link-underline;
|
|
151
145
|
}
|
|
152
146
|
}
|
|
153
147
|
|
|
148
|
+
details summary:active {
|
|
149
|
+
color: var(--link-active-color);
|
|
150
|
+
}
|
|
151
|
+
|
|
154
152
|
main details {
|
|
155
153
|
margin-bottom: 1rem;
|
|
156
154
|
border-radius: var(--border-radius);
|
package/src/_mixins.scss
CHANGED
|
@@ -42,4 +42,8 @@
|
|
|
42
42
|
-webkit-mask-position: center center;
|
|
43
43
|
mask-position: center center;
|
|
44
44
|
background-color: currentColor;
|
|
45
|
+
// The icon's visible fill is the background color, which browsers strip
|
|
46
|
+
// when printing by default. Preserve it so masked icons remain visible.
|
|
47
|
+
-webkit-print-color-adjust: exact;
|
|
48
|
+
print-color-adjust: exact;
|
|
45
49
|
}
|
package/src/header/index.ts
CHANGED
|
@@ -35,9 +35,21 @@ export default (window: Window) => {
|
|
|
35
35
|
}
|
|
36
36
|
window.addEventListener('keydown', handleWindowKeyDown);
|
|
37
37
|
|
|
38
|
+
function handleDetailsFocusOut(e: FocusEvent) {
|
|
39
|
+
if (!details.open) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const next = e.relatedTarget as Node | null;
|
|
43
|
+
if (!next || !details.contains(next)) {
|
|
44
|
+
close();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
details.addEventListener('focusout', handleDetailsFocusOut);
|
|
48
|
+
|
|
38
49
|
return () => {
|
|
39
50
|
window.removeEventListener('keydown', handleWindowKeyDown);
|
|
40
51
|
window.removeEventListener('click', handleWindowClick);
|
|
41
52
|
details.removeEventListener('click', handleDetailsClick);
|
|
53
|
+
details.removeEventListener('focusout', handleDetailsFocusOut);
|
|
42
54
|
};
|
|
43
55
|
};
|
package/src/print/style.scss
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
--print-font-size: 15px;
|
|
3
|
-
}
|
|
1
|
+
@use '../mixins' as *;
|
|
4
2
|
|
|
5
3
|
@media print {
|
|
6
4
|
html,
|
|
7
|
-
body
|
|
8
|
-
main,
|
|
9
|
-
section {
|
|
5
|
+
body {
|
|
10
6
|
background: none !important;
|
|
7
|
+
color: #000;
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
hr {
|
|
@@ -27,15 +24,22 @@
|
|
|
27
24
|
margin-top: 0;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
color: #000;
|
|
27
|
+
main a:not(.heading-anchor):not(.footnote-ref):not(.line-number) {
|
|
28
|
+
@include link-underline;
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
pre
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
pre,
|
|
32
|
+
tt,
|
|
33
|
+
:not(pre) > code {
|
|
34
|
+
border: 1px solid #ccc;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// .alert strips the default padding from inline code (since it also
|
|
38
|
+
// strips the background). In print we add a border, so restore the
|
|
39
|
+
// padding so the border doesn't hug the text.
|
|
40
|
+
.alert .content tt,
|
|
41
|
+
.alert .content code {
|
|
42
|
+
padding: var(--mono-padding);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
.footnotes {
|
|
@@ -50,18 +54,15 @@
|
|
|
50
54
|
display: none !important;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
main details .alert {
|
|
58
|
-
border: none;
|
|
59
|
-
padding: 0;
|
|
57
|
+
details,
|
|
58
|
+
.alert {
|
|
59
|
+
border-color: #000 !important;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
section {
|
|
63
63
|
border: none;
|
|
64
64
|
padding: 0;
|
|
65
|
+
margin: 0;
|
|
65
66
|
width: 100%;
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -69,7 +70,8 @@
|
|
|
69
70
|
display: none;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
.external-link-tail::after,
|
|
74
|
+
a.external:not(:has(.external-link-tail))::after {
|
|
75
|
+
content: none;
|
|
74
76
|
}
|
|
75
77
|
}
|
package/src/style.scss
CHANGED
|
@@ -91,14 +91,34 @@ a.external:not(:has(.external-link-tail))::after {
|
|
|
91
91
|
margin: 0;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
.footnotes ol {
|
|
95
|
+
list-style: none;
|
|
96
|
+
padding-left: 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
.footnotes li {
|
|
100
|
+
position: relative;
|
|
101
|
+
padding-left: 1.8em;
|
|
95
102
|
margin-bottom: 8px;
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
a.footnote-ref {
|
|
99
|
-
vertical-align:
|
|
100
|
-
border-bottom: none;
|
|
106
|
+
vertical-align: text-bottom;
|
|
101
107
|
font-size: 0.8em;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.footnote-marker {
|
|
112
|
+
@include user-select(none);
|
|
113
|
+
position: absolute;
|
|
114
|
+
left: 0;
|
|
115
|
+
top: 0;
|
|
116
|
+
font-weight: 500;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
a.footnote-ref,
|
|
120
|
+
.footnote-marker {
|
|
121
|
+
font-feature-settings: 'ss06';
|
|
102
122
|
}
|
|
103
123
|
|
|
104
124
|
.footnote-item {
|
|
@@ -108,7 +128,10 @@ a.footnote-ref {
|
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
|
|
111
|
-
a.footnote-ref
|
|
131
|
+
a.footnote-ref {
|
|
132
|
+
@include user-select(none);
|
|
133
|
+
}
|
|
134
|
+
|
|
112
135
|
.footnotes .footnote-backref {
|
|
113
136
|
display: inline-block;
|
|
114
137
|
@include user-select(none);
|
package/src/toc/style.scss
CHANGED
|
@@ -61,10 +61,11 @@ body.code .toc ol a {
|
|
|
61
61
|
.toc ol li:has(a):hover a {
|
|
62
62
|
color: var(--fg2-color);
|
|
63
63
|
}
|
|
64
|
+
}
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
.toc ol li:has(a):active,
|
|
67
|
+
.toc ol li:has(a):active a {
|
|
68
|
+
color: var(--link-active-color);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
@media (min-width: 900px) {
|