@abreen/tada 1.0.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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +290 -0
  3. package/bin/tada.js +361 -0
  4. package/config/authors.json +1 -0
  5. package/config/nav.json +28 -0
  6. package/content/index.md +19 -0
  7. package/content/lectures/01/Pair.java.md +296 -0
  8. package/content/lectures/01/Rectangle.java +80 -0
  9. package/content/lectures/01/demo.py +9 -0
  10. package/content/lectures/01/index.md +39 -0
  11. package/content/lectures/01/lecture1.pdf +0 -0
  12. package/content/lectures/index.md +25 -0
  13. package/content/markdown.md +379 -0
  14. package/content/problem_sets/index.md +6 -0
  15. package/fonts/google-sans-code/GoogleSansCodeVariable-Italic.ttf +0 -0
  16. package/fonts/google-sans-code/GoogleSansCodeVariable.ttf +0 -0
  17. package/fonts/google-sans-code/LICENSE.txt +93 -0
  18. package/fonts/inter/InterVariable-Italic.ttf +0 -0
  19. package/fonts/inter/InterVariable.ttf +0 -0
  20. package/fonts/inter/LICENSE.txt +92 -0
  21. package/package.json +70 -0
  22. package/public/avatars/alex.jpg +0 -0
  23. package/public/test.txt +1 -0
  24. package/src/_mixins.scss +4 -0
  25. package/src/anchor/README.md +6 -0
  26. package/src/anchor/index.ts +34 -0
  27. package/src/anchor/style.scss +48 -0
  28. package/src/code/README.md +5 -0
  29. package/src/code/index.ts +113 -0
  30. package/src/code/style.scss +101 -0
  31. package/src/code.scss +54 -0
  32. package/src/header/README.md +8 -0
  33. package/src/header/index.ts +43 -0
  34. package/src/header/style.scss +228 -0
  35. package/src/index.ts +73 -0
  36. package/src/layout.scss +144 -0
  37. package/src/literate/style.scss +60 -0
  38. package/src/print/README.md +4 -0
  39. package/src/print/index.ts +32 -0
  40. package/src/print/style.scss +82 -0
  41. package/src/question/README.md +3 -0
  42. package/src/question/index.ts +25 -0
  43. package/src/question/style.scss +116 -0
  44. package/src/search/README.md +6 -0
  45. package/src/search/index.ts +574 -0
  46. package/src/search/style.scss +217 -0
  47. package/src/style.scss +815 -0
  48. package/src/timezone/index.test.ts +100 -0
  49. package/src/timezone/index.ts +298 -0
  50. package/src/timezone/style.scss +16 -0
  51. package/src/timezone/timezones.json +58 -0
  52. package/src/toc/README.md +3 -0
  53. package/src/toc/index.ts +322 -0
  54. package/src/toc/style.scss +203 -0
  55. package/src/top/README.md +4 -0
  56. package/src/top/index.ts +75 -0
  57. package/src/util.ts +122 -0
  58. package/templates/_author.html +27 -0
  59. package/templates/_bottom.html +3 -0
  60. package/templates/_download.html +1 -0
  61. package/templates/_heading.html +19 -0
  62. package/templates/_nav.html +18 -0
  63. package/templates/_theme.scss +97 -0
  64. package/templates/_top.html +87 -0
  65. package/templates/authors.schema.json +13 -0
  66. package/templates/code.html +31 -0
  67. package/templates/default.html +13 -0
  68. package/templates/literate.html +16 -0
  69. package/templates/nav.schema.json +27 -0
  70. package/tsconfig.json +15 -0
  71. package/types/dev.ts +3 -0
  72. package/types/sass.d.ts +1 -0
  73. package/types/site-variables.d.ts +16 -0
  74. package/webpack/apply-base-path-plugin.js +78 -0
  75. package/webpack/build-state.js +97 -0
  76. package/webpack/code.test.js +162 -0
  77. package/webpack/colors.js +15 -0
  78. package/webpack/config.base.js +147 -0
  79. package/webpack/config.dev.js +23 -0
  80. package/webpack/config.prod.js +32 -0
  81. package/webpack/content-watch-plugin.js +153 -0
  82. package/webpack/deflist-id-plugin.js +62 -0
  83. package/webpack/external-links-plugin.js +37 -0
  84. package/webpack/features.js +5 -0
  85. package/webpack/flair.json +1 -0
  86. package/webpack/generate-content-assets-plugin.js +308 -0
  87. package/webpack/generate-favicon-plugin.js +198 -0
  88. package/webpack/generate-fonts-plugin.js +69 -0
  89. package/webpack/generate-manifest-plugin.js +116 -0
  90. package/webpack/globals.js +74 -0
  91. package/webpack/heading-subtitle-plugin.js +80 -0
  92. package/webpack/json-schema.js +19 -0
  93. package/webpack/log.js +143 -0
  94. package/webpack/markdown-plugins.test.js +203 -0
  95. package/webpack/pagefind-plugin.js +379 -0
  96. package/webpack/pagefind-plugin.test.js +131 -0
  97. package/webpack/pdf-text.js +163 -0
  98. package/webpack/print-flair-plugin.js +22 -0
  99. package/webpack/reachability.js +273 -0
  100. package/webpack/reachability.test.js +80 -0
  101. package/webpack/serve.js +104 -0
  102. package/webpack/site-variables.js +53 -0
  103. package/webpack/site.schema.json +67 -0
  104. package/webpack/templates.js +128 -0
  105. package/webpack/text-to-id.js +8 -0
  106. package/webpack/toc-plugin.js +167 -0
  107. package/webpack/util.js +49 -0
  108. package/webpack/utils/code.js +439 -0
  109. package/webpack/utils/content-files.js +147 -0
  110. package/webpack/utils/define-plugin.js +20 -0
  111. package/webpack/utils/file-types.js +26 -0
  112. package/webpack/utils/front-matter.js +57 -0
  113. package/webpack/utils/jdi-runner/LiterateRunner.class +0 -0
  114. package/webpack/utils/jdi-runner/LiterateRunner.java +241 -0
  115. package/webpack/utils/literate-java.js +153 -0
  116. package/webpack/utils/markdown.js +244 -0
  117. package/webpack/utils/parse-hsl.js +8 -0
  118. package/webpack/utils/paths.js +58 -0
  119. package/webpack/utils/render.js +466 -0
  120. package/webpack/utils/shiki-highlighter.js +26 -0
  121. package/webpack/validate-internal-links-plugin.js +155 -0
  122. package/webpack/watch-reachability-state.js +273 -0
  123. package/webpack/watch-reachability-state.test.js +198 -0
  124. package/webpack/watch-reload-client.js +54 -0
  125. package/webpack/watch.js +166 -0
@@ -0,0 +1,144 @@
1
+ @import './_mixins.scss';
2
+
3
+ :root {
4
+ --toc-width: 260px;
5
+ }
6
+
7
+ html {
8
+ margin: 0;
9
+ padding: 0;
10
+ overflow-y: auto;
11
+ scroll-padding-top: calc(var(--header-collapsed-height) + var(--gap));
12
+ }
13
+ body {
14
+ margin: 0;
15
+ padding: 0;
16
+ }
17
+
18
+ body > .container {
19
+ position: relative;
20
+ padding: 0 var(--gap);
21
+ margin-top: calc(var(--header-collapsed-height) + var(--gap));
22
+ }
23
+
24
+ .body {
25
+ padding-bottom: 2rem;
26
+ }
27
+
28
+ body.default .body,
29
+ body.literate .body {
30
+ > *:first-child {
31
+ margin-top: 0;
32
+ }
33
+ }
34
+
35
+ body.default .title-and-info,
36
+ body.default nav.toc,
37
+ body.literate .title-and-info,
38
+ body.literate nav.toc {
39
+ margin-bottom: 2rem;
40
+ }
41
+
42
+ body.default .title-and-info,
43
+ body.literate .title-and-info {
44
+ h1 {
45
+ margin: 0;
46
+ }
47
+ }
48
+
49
+ body.default main table,
50
+ body.literate main table {
51
+ width: 100%;
52
+ }
53
+
54
+ @media (max-width: 899px) {
55
+ .toc-is-active .main-content {
56
+ display: flex;
57
+ flex-direction: column;
58
+ }
59
+
60
+ .toc-is-active .main-content > .title-and-info {
61
+ order: 0;
62
+ }
63
+
64
+ .toc-is-active .main-content > nav.toc {
65
+ order: 1;
66
+ }
67
+
68
+ .toc-is-active .main-content > .body {
69
+ order: 2;
70
+ }
71
+
72
+ body.default.toc-is-active nav.toc,
73
+ body.literate.toc-is-active nav.toc {
74
+ background: var(--bg2-color);
75
+ padding: var(--gap);
76
+ }
77
+
78
+ body.code.toc-is-active nav.toc {
79
+ display: none;
80
+ }
81
+ }
82
+
83
+ @media (min-width: 900px) {
84
+ body.default main table,
85
+ body.literate main table {
86
+ width: auto;
87
+ min-width: 400px;
88
+ margin: 1rem auto;
89
+ }
90
+
91
+ :not(.toc-is-active) > .container > .main-content {
92
+ max-width: var(--main-width);
93
+ margin: 0 auto;
94
+ }
95
+
96
+ .toc-is-active .main-content {
97
+ display: flex;
98
+ flex-wrap: wrap;
99
+ gap: 0 var(--gap);
100
+ }
101
+
102
+ .toc-is-active .main-content > .title-and-info {
103
+ flex: 0 0 100%;
104
+ }
105
+
106
+ .toc-is-active .main-content > .body {
107
+ order: 1;
108
+ flex: 1;
109
+ min-width: 0;
110
+ }
111
+
112
+ body.toc-is-active nav.toc {
113
+ box-sizing: border-box;
114
+ width: var(--toc-width);
115
+ flex-shrink: 0;
116
+ align-self: flex-start;
117
+ position: sticky;
118
+ top: var(--header-collapsed-height);
119
+ height: calc(100vh - var(--header-collapsed-height));
120
+ overflow-y: auto;
121
+ margin-bottom: 0;
122
+ }
123
+
124
+ body.code.toc-is-active nav.toc {
125
+ top: calc(var(--header-collapsed-height) + var(--gap));
126
+ height: calc(100vh - var(--header-collapsed-height) - var(--gap));
127
+ }
128
+ }
129
+
130
+ @media (min-width: 1300px) {
131
+ .toc-is-active .main-content {
132
+ max-width: calc(var(--toc-width) + var(--gap) + 960px);
133
+ margin: 0 auto;
134
+ }
135
+ }
136
+
137
+ .columns {
138
+ display: grid;
139
+ grid-template-columns: max-content auto;
140
+ gap: 1rem;
141
+ &.split {
142
+ grid-template-columns: repeat(auto-fit, minmax(300px, auto));
143
+ }
144
+ }
@@ -0,0 +1,60 @@
1
+ body.literate .body pre:has(.code-row) {
2
+ background: var(--bg2-color);
3
+ border-radius: var(--mono-border-radius);
4
+ overflow-x: auto;
5
+ overscroll-behavior-x: none;
6
+ padding: 0;
7
+ -webkit-text-size-adjust: 100%;
8
+ text-size-adjust: 100%;
9
+
10
+ code {
11
+ padding-right: var(--gap);
12
+ }
13
+
14
+ .code-row {
15
+ &:first-child {
16
+ padding-top: var(--gap);
17
+ }
18
+
19
+ &:last-child {
20
+ padding-bottom: var(--gap);
21
+ }
22
+ }
23
+ }
24
+
25
+ body.literate .literate-code-output {
26
+ display: grid;
27
+ grid-template-columns: 1fr;
28
+ gap: var(--gap);
29
+ font-size: var(--mono-font-size);
30
+
31
+ > pre {
32
+ margin-top: 0;
33
+ }
34
+
35
+ > pre:last-child {
36
+ white-space: pre-wrap;
37
+ }
38
+
39
+ @media (max-width: 899px) {
40
+ > pre:first-child {
41
+ margin-bottom: 0;
42
+ }
43
+
44
+ > pre:last-child::before {
45
+ content: 'Output';
46
+ display: block;
47
+ font-size: var(--font-size-small);
48
+ font-style: italic;
49
+ color: var(--fg2-color);
50
+ }
51
+ }
52
+
53
+ @media (min-width: 900px) {
54
+ grid-template-columns: 1fr 1fr;
55
+
56
+ > pre:last-child {
57
+ min-width: 150px;
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,4 @@
1
+ # The `print` component
2
+
3
+ If the user is printing, force all `<details>` elements to be
4
+ open, then restore their collapsed state afterwards.
@@ -0,0 +1,32 @@
1
+ export default (window: Window) => {
2
+ function getDetailsElements(parent: HTMLElement): HTMLDetailsElement[] {
3
+ return Array.from(parent.querySelectorAll('details')).filter(
4
+ el => el?.parentElement?.tagName?.toLowerCase() !== 'header',
5
+ );
6
+ }
7
+
8
+ function handleBeforePrint() {
9
+ for (const el of getDetailsElements(window.document.body)) {
10
+ if (el.getAttribute('open') == null) {
11
+ el.setAttribute('data-was-closed', 'true');
12
+ }
13
+ el.setAttribute('open', '');
14
+ }
15
+ }
16
+ window.addEventListener('beforeprint', handleBeforePrint);
17
+
18
+ function handleAfterPrint() {
19
+ for (const el of getDetailsElements(window.document.body)) {
20
+ if (el.getAttribute('data-was-closed') != null) {
21
+ el.removeAttribute('data-was-closed');
22
+ el.removeAttribute('open');
23
+ }
24
+ }
25
+ }
26
+ window.addEventListener('afterprint', handleAfterPrint);
27
+
28
+ return () => {
29
+ window.removeEventListener('afterprint', handleAfterPrint);
30
+ window.removeEventListener('beforeprint', handleBeforePrint);
31
+ };
32
+ };
@@ -0,0 +1,82 @@
1
+ :root {
2
+ --print-font-size: 15px;
3
+ }
4
+
5
+ @media print {
6
+ html,
7
+ body,
8
+ main,
9
+ section {
10
+ background: none !important;
11
+ }
12
+
13
+ main {
14
+ padding-top: 0;
15
+ }
16
+
17
+ hr {
18
+ height: 0;
19
+ border-bottom: 2px solid gray;
20
+
21
+ &::after {
22
+ display: none;
23
+ }
24
+ }
25
+
26
+ header {
27
+ display: none !important;
28
+ }
29
+
30
+ main {
31
+ max-width: none;
32
+ width: 100%;
33
+ margin: 0;
34
+ padding: 0;
35
+ }
36
+
37
+ body {
38
+ font-size: var(--print-font-size);
39
+ color: #000;
40
+ }
41
+
42
+ pre {
43
+ font-size: var(--print-font-size);
44
+ border: none;
45
+ padding: 0;
46
+ }
47
+
48
+ .footnotes {
49
+ break-inside: avoid;
50
+ }
51
+
52
+ a.footnote-ref {
53
+ text-decoration: none;
54
+ }
55
+
56
+ .footnote-backref {
57
+ display: none !important;
58
+ }
59
+
60
+ main details {
61
+ border: 2px solid #000;
62
+ }
63
+
64
+ main details .alert {
65
+ border: none;
66
+ padding: 0;
67
+ }
68
+
69
+ section {
70
+ border: none;
71
+ padding: 0;
72
+ width: 100%;
73
+ }
74
+
75
+ .heading-anchor::after {
76
+ display: none;
77
+ }
78
+
79
+ a.external::after {
80
+ display: none;
81
+ }
82
+ }
@@ -0,0 +1,3 @@
1
+ # The `question` component
2
+
3
+ Handles the click behavior of Q&A blocks whose answers are hidden by default.
@@ -0,0 +1,25 @@
1
+ export default (window: Window) => {
2
+ const bodies =
3
+ window.document.querySelectorAll<HTMLElement>('.question-a-body');
4
+ bodies.forEach(el => {
5
+ el.setAttribute('role', 'button');
6
+ el.setAttribute('tabindex', '0');
7
+ el.setAttribute('aria-label', 'Click to reveal answer');
8
+
9
+ const reveal = () => {
10
+ el.setAttribute('data-revealed', '');
11
+ el.removeAttribute('role');
12
+ el.removeAttribute('tabindex');
13
+ el.removeAttribute('aria-label');
14
+ };
15
+
16
+ el.addEventListener('click', reveal);
17
+ el.addEventListener('keydown', (e: KeyboardEvent) => {
18
+ if (e.key === 'Enter' || e.key === ' ') {
19
+ e.preventDefault();
20
+ reveal();
21
+ }
22
+ });
23
+ });
24
+ return () => {};
25
+ };
@@ -0,0 +1,116 @@
1
+ @import '../_mixins.scss';
2
+
3
+ .question {
4
+ margin-bottom: 2rem;
5
+ }
6
+
7
+ .question-q,
8
+ .question-a {
9
+ display: flex;
10
+ gap: 0.5em;
11
+ align-items: flex-start;
12
+ }
13
+
14
+ .question-q {
15
+ margin-bottom: 0.5em;
16
+ }
17
+
18
+ .question-label,
19
+ .question-a-label {
20
+ color: var(--theme-color-text);
21
+ font-weight: 700;
22
+ flex-shrink: 0;
23
+ margin: 0;
24
+ @include user-select(none);
25
+ }
26
+
27
+ .question-q > span:last-child {
28
+ color: var(--fg-color);
29
+ font-weight: var(--font-weight);
30
+ }
31
+
32
+ .question-a-body {
33
+ flex: 1;
34
+ cursor: pointer;
35
+ @include user-select(none);
36
+ }
37
+
38
+ .question-a-body > ol:first-child,
39
+ .question-a-body > ul:first-child {
40
+ margin-top: 0;
41
+ }
42
+
43
+ .question-a-body > *:last-child {
44
+ margin-bottom: 0;
45
+ }
46
+
47
+ .question-a-body > *,
48
+ .question-a-body > * * {
49
+ @media (prefers-reduced-motion: no-preference) {
50
+ transition:
51
+ color 0.25s,
52
+ background-color 0.25s;
53
+ }
54
+ }
55
+
56
+ .question-a-body:not([data-revealed]) > * {
57
+ color: transparent;
58
+ background-color: var(--bg2-color);
59
+ pointer-events: none;
60
+ }
61
+
62
+ .question-a-body:not([data-revealed]) > * * {
63
+ color: transparent;
64
+ background-color: var(--bg2-color);
65
+ }
66
+
67
+ .question-a-body::after {
68
+ content: 'Click to reveal';
69
+ display: block;
70
+ font-size: var(--font-size-small);
71
+ font-style: italic;
72
+ color: var(--fg2-color);
73
+ text-align: center;
74
+ pointer-events: none;
75
+
76
+ @media (prefers-reduced-motion: no-preference) {
77
+ transition: opacity 0.25s;
78
+ }
79
+ }
80
+
81
+ .question-a-body[data-revealed]::after {
82
+ opacity: 0;
83
+ }
84
+
85
+ @media (hover: none) {
86
+ .question-a-body::after {
87
+ content: 'Tap to reveal';
88
+ }
89
+ }
90
+
91
+ .question-a-body[data-revealed] {
92
+ cursor: unset;
93
+ @include user-select(text);
94
+ }
95
+
96
+ @media print {
97
+ .question-a-body {
98
+ cursor: default;
99
+ @include user-select(text);
100
+
101
+ &:not([data-revealed]) > * {
102
+ color: inherit;
103
+ background-color: transparent;
104
+ pointer-events: auto;
105
+ }
106
+
107
+ &:not([data-revealed]) > * * {
108
+ color: inherit;
109
+ background-color: transparent;
110
+ }
111
+
112
+ &::after {
113
+ display: none;
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,6 @@
1
+ # The `search` component
2
+
3
+ The client-side logic that controls the `<input type="search">` element
4
+ on the page and uses the [Pagefind][pagefind] index to provide results.
5
+
6
+ [pagefind]: https://pagefind.app/