@abreen/tada 1.1.0 → 1.2.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 (88) hide show
  1. package/README.md +68 -16
  2. package/bin/tada.ts +289 -24
  3. package/bin/validators.test.ts +4 -3
  4. package/bin/validators.ts +4 -2
  5. package/build/apply-base-path-plugin.ts +24 -13
  6. package/build/build-manifest.test.ts +349 -0
  7. package/build/build-manifest.ts +165 -0
  8. package/build/bundle.ts +19 -20
  9. package/build/code.test.ts +7 -3
  10. package/build/content-watch.ts +6 -6
  11. package/build/copy.ts +2 -2
  12. package/build/deflist-id-plugin.ts +1 -1
  13. package/build/external-links-plugin.ts +2 -2
  14. package/build/features.ts +1 -1
  15. package/build/generate-content-assets.ts +12 -7
  16. package/build/generate-favicon.ts +13 -6
  17. package/build/generate-fonts.ts +2 -2
  18. package/build/generate-katex-assets.ts +35 -0
  19. package/build/generate-manifest.ts +2 -2
  20. package/build/globals.test.ts +9 -11
  21. package/build/globals.ts +6 -7
  22. package/build/json-schema.test.ts +1 -1
  23. package/build/log.test.ts +1 -1
  24. package/build/log.ts +2 -2
  25. package/build/markdown-plugins.test.ts +118 -10
  26. package/build/pagefind.test.ts +2 -2
  27. package/build/pagefind.ts +5 -5
  28. package/build/pdf-text.test.ts +52 -0
  29. package/build/pipeline.ts +42 -15
  30. package/build/reachability.test.ts +1 -1
  31. package/build/serve.ts +3 -3
  32. package/build/site-variables.ts +13 -5
  33. package/build/templates.ts +7 -6
  34. package/build/toc-plugin.test.ts +2 -2
  35. package/build/toc-plugin.ts +1 -1
  36. package/build/types.ts +15 -6
  37. package/build/util.ts +6 -6
  38. package/build/utils/code.ts +5 -5
  39. package/build/utils/content-files.ts +3 -3
  40. package/build/utils/derive-theme.test.ts +1 -1
  41. package/build/utils/derive-theme.ts +1 -1
  42. package/build/utils/file-types.test.ts +1 -1
  43. package/build/utils/front-matter.test.ts +1 -4
  44. package/build/utils/front-matter.ts +2 -2
  45. package/build/utils/literate-java.ts +11 -10
  46. package/build/utils/markdown.ts +41 -9
  47. package/build/utils/paths.test.ts +2 -2
  48. package/build/utils/paths.ts +5 -1
  49. package/build/utils/render.ts +45 -32
  50. package/build/utils/shiki-highlighter.ts +1 -1
  51. package/build/validate-internal-links-plugin.test.ts +1 -1
  52. package/build/validate-internal-links-plugin.ts +1 -1
  53. package/build/watch-reachability-state.test.ts +2 -2
  54. package/build/watch-reachability-state.ts +1 -1
  55. package/build/watch.ts +74 -98
  56. package/{content → init/content}/lectures/index.md +1 -1
  57. package/{content → init/content}/markdown.md +26 -7
  58. package/package.json +9 -7
  59. package/{templates → schema}/nav.schema.json +1 -2
  60. package/{build → schema}/site.schema.json +1 -2
  61. package/src/_alerts.scss +4 -0
  62. package/src/_base.scss +41 -2
  63. package/src/_layout.scss +37 -0
  64. package/src/anchor/index.ts +0 -1
  65. package/src/index.ts +3 -3
  66. package/src/search/index.test.ts +203 -0
  67. package/src/search/index.ts +3 -79
  68. package/src/search/pdf-utils.ts +79 -0
  69. package/src/search/style.scss +0 -14
  70. package/src/style.scss +9 -57
  71. package/src/timezone/index.ts +5 -6
  72. package/src/util.ts +1 -1
  73. package/templates/_top.html +1 -1
  74. package/types/dev.d.ts +1 -3
  75. package/types/site-variables.d.ts +6 -10
  76. package/types/untyped-modules.d.ts +7 -0
  77. package/config/authors.json +0 -1
  78. package/config/nav.json +0 -28
  79. /package/{content → init/content}/index.md +0 -0
  80. /package/{content → init/content}/lectures/01/Pair.java.md +0 -0
  81. /package/{content → init/content}/lectures/01/Rectangle.java +0 -0
  82. /package/{content → init/content}/lectures/01/demo.py +0 -0
  83. /package/{content → init/content}/lectures/01/index.md +0 -0
  84. /package/{content → init/content}/lectures/01/lecture1.pdf +0 -0
  85. /package/{content → init/content}/problem_sets/index.html +0 -0
  86. /package/{public → init/public}/avatars/alex.jpg +0 -0
  87. /package/{public → init/public}/test.txt +0 -0
  88. /package/{templates → schema}/authors.schema.json +0 -0
@@ -0,0 +1,203 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { getPdfPageNumber, getPdfBaseUrl, groupPdfResults } from './pdf-utils';
3
+ import type { Result } from './pdf-utils';
4
+
5
+ describe('getPdfPageNumber', () => {
6
+ test('returns page number from meta string', () => {
7
+ expect(getPdfPageNumber('/doc.pdf#page=3', '3')).toBe(3);
8
+ });
9
+
10
+ test('prefers meta over URL hash', () => {
11
+ expect(getPdfPageNumber('/doc.pdf#page=5', '2')).toBe(2);
12
+ });
13
+
14
+ test('falls back to URL hash when meta is undefined', () => {
15
+ expect(getPdfPageNumber('/doc.pdf#page=7', undefined)).toBe(7);
16
+ });
17
+
18
+ test('falls back to URL hash when meta is empty', () => {
19
+ expect(getPdfPageNumber('/doc.pdf#page=4', '')).toBe(4);
20
+ });
21
+
22
+ test('falls back to URL hash when meta is not a number', () => {
23
+ expect(getPdfPageNumber('/doc.pdf#page=4', 'abc')).toBe(4);
24
+ });
25
+
26
+ test('returns null when neither meta nor hash has a page', () => {
27
+ expect(getPdfPageNumber('/doc.pdf', undefined)).toBeNull();
28
+ });
29
+
30
+ test('returns null for non-positive meta page number', () => {
31
+ expect(getPdfPageNumber('/doc.pdf', '0')).toBeNull();
32
+ });
33
+
34
+ test('returns null for negative meta page number', () => {
35
+ expect(getPdfPageNumber('/doc.pdf', '-1')).toBeNull();
36
+ });
37
+
38
+ test('returns null for hash page=0', () => {
39
+ expect(getPdfPageNumber('/doc.pdf#page=0', undefined)).toBeNull();
40
+ });
41
+
42
+ test('parses page from hash with other params', () => {
43
+ expect(getPdfPageNumber('/doc.pdf#zoom=100&page=12', undefined)).toBe(12);
44
+ });
45
+
46
+ test('handles case-insensitive page hash', () => {
47
+ expect(getPdfPageNumber('/doc.pdf#Page=9', undefined)).toBe(9);
48
+ });
49
+ });
50
+
51
+ describe('getPdfBaseUrl', () => {
52
+ test('returns base URL for a PDF with hash', () => {
53
+ expect(getPdfBaseUrl('/docs/guide.pdf#page=3')).toBe('/docs/guide.pdf');
54
+ });
55
+
56
+ test('returns URL unchanged when no hash present', () => {
57
+ expect(getPdfBaseUrl('/docs/guide.pdf')).toBe('/docs/guide.pdf');
58
+ });
59
+
60
+ test('returns null for non-PDF URL', () => {
61
+ expect(getPdfBaseUrl('/about/')).toBeNull();
62
+ });
63
+
64
+ test('returns null for non-PDF URL with hash', () => {
65
+ expect(getPdfBaseUrl('/about/#section')).toBeNull();
66
+ });
67
+
68
+ test('handles .PDF extension case-insensitively', () => {
69
+ expect(getPdfBaseUrl('/docs/GUIDE.PDF#page=1')).toBe('/docs/GUIDE.PDF');
70
+ });
71
+ });
72
+
73
+ describe('groupPdfResults', () => {
74
+ function makeResult(overrides: Partial<Result>): Result {
75
+ return {
76
+ title: 'doc.pdf',
77
+ url: '/doc.pdf',
78
+ excerpt: '',
79
+ score: 1,
80
+ subResults: [],
81
+ pageNumber: null,
82
+ ...overrides,
83
+ };
84
+ }
85
+
86
+ test('passes through non-PDF results unchanged', () => {
87
+ const results: Result[] = [
88
+ makeResult({ title: 'About', url: '/about/', score: 5 }),
89
+ makeResult({ title: 'Home', url: '/', score: 3 }),
90
+ ];
91
+ const grouped = groupPdfResults(results);
92
+ expect(grouped).toEqual(results);
93
+ });
94
+
95
+ test('groups multiple PDF page results into one result with sub-results', () => {
96
+ const results: Result[] = [
97
+ makeResult({
98
+ url: '/doc.pdf#page=3',
99
+ score: 10,
100
+ pageNumber: 3,
101
+ excerpt: 'page 3 text',
102
+ }),
103
+ makeResult({
104
+ url: '/doc.pdf#page=1',
105
+ score: 5,
106
+ pageNumber: 1,
107
+ excerpt: 'page 1 text',
108
+ }),
109
+ ];
110
+ const grouped = groupPdfResults(results);
111
+ expect(grouped).toHaveLength(1);
112
+ expect(grouped[0].url).toBe('/doc.pdf');
113
+ expect(grouped[0].score).toBe(10);
114
+ expect(grouped[0].subResults).toEqual([
115
+ { title: 'Page 1', url: '/doc.pdf#page=1', excerpt: 'page 1 text' },
116
+ { title: 'Page 3', url: '/doc.pdf#page=3', excerpt: 'page 3 text' },
117
+ ]);
118
+ });
119
+
120
+ test('sub-results are sorted by page number', () => {
121
+ const results: Result[] = [
122
+ makeResult({ url: '/doc.pdf#page=5', score: 1, pageNumber: 5 }),
123
+ makeResult({ url: '/doc.pdf#page=2', score: 8, pageNumber: 2 }),
124
+ makeResult({ url: '/doc.pdf#page=9', score: 3, pageNumber: 9 }),
125
+ ];
126
+ const grouped = groupPdfResults(results);
127
+ expect(grouped[0].subResults.map(s => s.title)).toEqual([
128
+ 'Page 2',
129
+ 'Page 5',
130
+ 'Page 9',
131
+ ]);
132
+ });
133
+
134
+ test('primary result uses highest-scoring page', () => {
135
+ const results: Result[] = [
136
+ makeResult({
137
+ url: '/doc.pdf#page=1',
138
+ score: 2,
139
+ pageNumber: 1,
140
+ excerpt: 'low',
141
+ }),
142
+ makeResult({
143
+ url: '/doc.pdf#page=4',
144
+ score: 9,
145
+ pageNumber: 4,
146
+ excerpt: 'high',
147
+ }),
148
+ ];
149
+ const grouped = groupPdfResults(results);
150
+ expect(grouped[0].excerpt).toBe('high');
151
+ expect(grouped[0].score).toBe(9);
152
+ });
153
+
154
+ test('mixes non-PDF and grouped PDF results sorted by score', () => {
155
+ const results: Result[] = [
156
+ makeResult({
157
+ title: 'About',
158
+ url: '/about/',
159
+ score: 7,
160
+ pageNumber: null,
161
+ }),
162
+ makeResult({ url: '/doc.pdf#page=1', score: 10, pageNumber: 1 }),
163
+ makeResult({ url: '/doc.pdf#page=2', score: 3, pageNumber: 2 }),
164
+ ];
165
+ const grouped = groupPdfResults(results);
166
+ expect(grouped).toHaveLength(2);
167
+ expect(grouped[0].url).toBe('/doc.pdf');
168
+ expect(grouped[0].score).toBe(10);
169
+ expect(grouped[1].url).toBe('/about/');
170
+ expect(grouped[1].score).toBe(7);
171
+ });
172
+
173
+ test('does not group PDF result without a page number', () => {
174
+ const results: Result[] = [
175
+ makeResult({ url: '/doc.pdf', score: 5, pageNumber: null }),
176
+ ];
177
+ const grouped = groupPdfResults(results);
178
+ expect(grouped).toHaveLength(1);
179
+ expect(grouped[0].url).toBe('/doc.pdf');
180
+ expect(grouped[0].subResults).toEqual([]);
181
+ });
182
+
183
+ test('groups results from different PDFs separately', () => {
184
+ const results: Result[] = [
185
+ makeResult({
186
+ title: 'a.pdf',
187
+ url: '/a.pdf#page=1',
188
+ score: 5,
189
+ pageNumber: 1,
190
+ }),
191
+ makeResult({
192
+ title: 'b.pdf',
193
+ url: '/b.pdf#page=1',
194
+ score: 8,
195
+ pageNumber: 1,
196
+ }),
197
+ ];
198
+ const grouped = groupPdfResults(results);
199
+ expect(grouped).toHaveLength(2);
200
+ const urls = grouped.map(r => r.url).sort();
201
+ expect(urls).toEqual(['/a.pdf', '/b.pdf']);
202
+ });
203
+ });
@@ -1,4 +1,6 @@
1
1
  import { getElement, applyBasePath } from '../util';
2
+ import { getPdfPageNumber, groupPdfResults } from './pdf-utils';
3
+ import type { Result } from './pdf-utils';
2
4
 
3
5
  const MAX_RESULTS = 24;
4
6
 
@@ -28,14 +30,6 @@ interface Pagefind {
28
30
  let pagefind: Pagefind | null = null;
29
31
 
30
32
  type SubResult = { title: string; url: string; excerpt: string };
31
- type Result = {
32
- title: string;
33
- url: string;
34
- excerpt: string;
35
- score: number;
36
- subResults: SubResult[];
37
- pageNumber: number | null;
38
- };
39
33
 
40
34
  type State = {
41
35
  value: string;
@@ -44,76 +38,6 @@ type State = {
44
38
  totalResults: number;
45
39
  };
46
40
 
47
- function getPdfPageNumber(
48
- url: string,
49
- pageMeta: string | undefined,
50
- ): number | null {
51
- const fromMeta = Number.parseInt(pageMeta ?? '', 10);
52
- if (Number.isInteger(fromMeta) && fromMeta > 0) {
53
- return fromMeta;
54
- }
55
- const match = url.match(/#(?:.*&)?page=(\d+)\b/i);
56
- if (!match) {
57
- return null;
58
- }
59
- const fromUrl = Number.parseInt(match[1], 10);
60
- return Number.isInteger(fromUrl) && fromUrl >= 1 ? fromUrl : null;
61
- }
62
-
63
- function getPdfBaseUrl(url: string): string | null {
64
- const hashIndex = url.indexOf('#');
65
- const baseUrl = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
66
- return baseUrl.toLowerCase().endsWith('.pdf') ? baseUrl : null;
67
- }
68
-
69
- function groupPdfResults(results: Result[]): Result[] {
70
- const pdfGroups = new Map<string, Result[]>();
71
- const other: Result[] = [];
72
-
73
- for (const result of results) {
74
- const baseUrl = getPdfBaseUrl(result.url);
75
- if (!baseUrl || result.pageNumber == null) {
76
- other.push(result);
77
- continue;
78
- }
79
- if (!pdfGroups.has(baseUrl)) {
80
- pdfGroups.set(baseUrl, []);
81
- }
82
- pdfGroups.get(baseUrl)!.push(result);
83
- }
84
-
85
- const grouped: Result[] = [...other];
86
-
87
- pdfGroups.forEach((pages, baseUrl) => {
88
- const primary = pages
89
- .slice()
90
- .sort(
91
- (a, b) =>
92
- b.score - a.score || (a.pageNumber ?? 0) - (b.pageNumber ?? 0),
93
- )[0];
94
- const subResults = pages
95
- .slice()
96
- .sort((a, b) => (a.pageNumber ?? 0) - (b.pageNumber ?? 0))
97
- .map(page => ({
98
- title: `Page ${page.pageNumber ?? '?'}`,
99
- url: page.url,
100
- excerpt: page.excerpt,
101
- }));
102
-
103
- grouped.push({
104
- title: primary.title,
105
- url: baseUrl,
106
- excerpt: primary.excerpt,
107
- score: primary.score,
108
- subResults,
109
- pageNumber: null,
110
- });
111
- });
112
-
113
- grouped.sort((a, b) => b.score - a.score);
114
- return grouped;
115
- }
116
-
117
41
  async function doSearch(state: State) {
118
42
  if (pagefind == null) {
119
43
  return;
@@ -129,7 +53,7 @@ async function doSearch(state: State) {
129
53
  const slice = search.results.slice(0, MAX_RESULTS);
130
54
  const data = await Promise.all(slice.map(r => r.data()));
131
55
 
132
- const titlePostfix = window.siteVariables.titlePostfix ?? '';
56
+ const titlePostfix = __SITE_TITLE_POSTFIX__;
133
57
  const results: Result[] = data.map((d: PagefindResult) => {
134
58
  let title: string = d.meta?.title ?? d.url;
135
59
  if (titlePostfix && title.endsWith(titlePostfix)) {
@@ -0,0 +1,79 @@
1
+ export type SubResult = { title: string; url: string; excerpt: string };
2
+ export type Result = {
3
+ title: string;
4
+ url: string;
5
+ excerpt: string;
6
+ score: number;
7
+ subResults: SubResult[];
8
+ pageNumber: number | null;
9
+ };
10
+
11
+ export function getPdfPageNumber(
12
+ url: string,
13
+ pageMeta: string | undefined,
14
+ ): number | null {
15
+ const fromMeta = Number.parseInt(pageMeta ?? '', 10);
16
+ if (Number.isInteger(fromMeta) && fromMeta > 0) {
17
+ return fromMeta;
18
+ }
19
+ const match = url.match(/#(?:.*&)?page=(\d+)\b/i);
20
+ if (!match) {
21
+ return null;
22
+ }
23
+ const fromUrl = Number.parseInt(match[1], 10);
24
+ return Number.isInteger(fromUrl) && fromUrl >= 1 ? fromUrl : null;
25
+ }
26
+
27
+ export function getPdfBaseUrl(url: string): string | null {
28
+ const hashIndex = url.indexOf('#');
29
+ const baseUrl = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
30
+ return baseUrl.toLowerCase().endsWith('.pdf') ? baseUrl : null;
31
+ }
32
+
33
+ export function groupPdfResults(results: Result[]): Result[] {
34
+ const pdfGroups = new Map<string, Result[]>();
35
+ const other: Result[] = [];
36
+
37
+ for (const result of results) {
38
+ const baseUrl = getPdfBaseUrl(result.url);
39
+ if (!baseUrl || result.pageNumber == null) {
40
+ other.push(result);
41
+ continue;
42
+ }
43
+ if (!pdfGroups.has(baseUrl)) {
44
+ pdfGroups.set(baseUrl, []);
45
+ }
46
+ pdfGroups.get(baseUrl)!.push(result);
47
+ }
48
+
49
+ const grouped: Result[] = [...other];
50
+
51
+ pdfGroups.forEach((pages, baseUrl) => {
52
+ const primary = pages
53
+ .slice()
54
+ .sort(
55
+ (a, b) =>
56
+ b.score - a.score || (a.pageNumber ?? 0) - (b.pageNumber ?? 0),
57
+ )[0];
58
+ const subResults = pages
59
+ .slice()
60
+ .sort((a, b) => (a.pageNumber ?? 0) - (b.pageNumber ?? 0))
61
+ .map(page => ({
62
+ title: `Page ${page.pageNumber ?? '?'}`,
63
+ url: page.url,
64
+ excerpt: page.excerpt,
65
+ }));
66
+
67
+ grouped.push({
68
+ title: primary.title,
69
+ url: baseUrl,
70
+ excerpt: primary.excerpt,
71
+ score: primary.score,
72
+ subResults,
73
+ pageNumber: null,
74
+ });
75
+ });
76
+
77
+ grouped.sort((a, b) => b.score - a.score);
78
+ return grouped;
79
+ }
@@ -8,21 +8,7 @@ header {
8
8
  gap: 0.5rem;
9
9
 
10
10
  input.search {
11
- background-color: var(--bg-color);
12
- background-image: var(--icon-search);
13
- background-repeat: no-repeat;
14
- background-position: left 6px center;
15
- background-size: 16px 16px;
16
- padding-left: 2rem;
17
-
18
- border-radius: 18px;
19
11
  flex: 1;
20
- min-width: 0;
21
-
22
- &:disabled {
23
- opacity: 0.4;
24
- cursor: not-allowed;
25
- }
26
12
  }
27
13
  }
28
14
 
package/src/style.scss CHANGED
@@ -47,60 +47,18 @@
47
47
  outline-offset: 1px;
48
48
  }
49
49
 
50
- .info {
51
- @include user-select(none);
52
- display: grid;
53
- grid-template-columns: 1fr auto;
54
- align-items: baseline;
55
- color: var(--fg2-color);
56
- font-size: var(--font-size-smaller);
57
-
58
- @media (width < 450px) {
59
- grid-template-columns: 1fr;
60
- }
61
-
62
- aside.author {
63
- a {
64
- text-decoration: none;
65
-
66
- .text {
67
- color: var(--fg2-color);
68
- text-decoration: underline;
69
- text-decoration-color: var(--fg2-color);
70
- text-decoration-thickness: 1px;
71
- text-underline-offset: 3px;
72
- }
73
- }
74
-
75
- .avatar {
76
- width: 24px;
77
- height: 24px;
78
- vertical-align: middle;
79
- margin-left: 4px;
80
- }
81
- }
82
- }
83
-
84
50
  a.disabled {
85
51
  color: var(--fg-color-translucent);
86
52
  text-decoration-color: var(--fg-color-translucent);
87
53
  cursor: not-allowed;
88
- }
89
-
90
- @media (hover: hover) {
91
- a:hover {
92
- color: var(--fg2-color);
93
- }
94
54
 
95
- a.disabled:hover {
96
- color: var(--fg-color-translucent);
55
+ @media (hover: hover) {
56
+ &:hover {
57
+ color: var(--fg-color-translucent);
58
+ }
97
59
  }
98
60
  }
99
61
 
100
- a:active {
101
- color: var(--link-active-color);
102
- }
103
-
104
62
  a.external::after {
105
63
  content: '';
106
64
  background: var(--icon-external-link);
@@ -233,16 +191,6 @@ main details {
233
191
  border: 2px solid var(--fg-color);
234
192
  }
235
193
 
236
- input[type='text'],
237
- input[type='search'] {
238
- background: var(--bg-color);
239
- color: var(--fg-color);
240
- border: var(--control-border-width) solid var(--fg2-color);
241
- border-radius: var(--border-radius);
242
- font-size: 12pt;
243
- padding: 4px 4px;
244
- }
245
-
246
194
  button,
247
195
  a.button,
248
196
  input[type='submit'] {
@@ -279,7 +227,7 @@ input[type='submit']:focus-visible,
279
227
  input[type='text']:focus-visible,
280
228
  input[type='search']:focus-visible {
281
229
  border-color: var(--fg-color);
282
- background: var(--bg2-color);
230
+ background-color: var(--bg2-color);
283
231
  }
284
232
 
285
233
  button.icon-button {
@@ -470,6 +418,10 @@ dl {
470
418
  text-decoration-color: var(--fg2-color);
471
419
  }
472
420
  }
421
+
422
+ &:active {
423
+ text-decoration-color: var(--link-active-color);
424
+ }
473
425
  }
474
426
  }
475
427
 
@@ -84,14 +84,13 @@ function getOffsetMinutes(tz: string, date: Date): number {
84
84
  return (utcTs - date.getTime()) / 60000;
85
85
  }
86
86
 
87
- // window.siteVariables.timezones is replaced by DefinePlugin at build time.
88
- // The typeof guard prevents a ReferenceError in test environments where
89
- // window is not defined.
90
- const TIMEZONES: TimezoneDef[] =
91
- typeof window !== 'undefined' ? window.siteVariables.timezones : [];
87
+ // typeof guard prevents ReferenceError when tests import this module
88
+ // without going through the bundler (which substitutes __SITE_TIMEZONES__).
89
+ const TIMEZONES: TimeZone[] =
90
+ typeof __SITE_TIMEZONES__ !== 'undefined' ? __SITE_TIMEZONES__ : [];
92
91
 
93
92
  function getDefaultTimezone() {
94
- const value = window.siteVariables.defaultTimeZone;
93
+ const value = __SITE_DEFAULT_TIMEZONE__;
95
94
  return TIMEZONES.find(tz => tz.value === value)!;
96
95
  }
97
96
 
package/src/util.ts CHANGED
@@ -34,7 +34,7 @@ export function applyBasePath(subPath: string): string {
34
34
  throw new Error('invalid internal path, must start with "/": ' + subPath);
35
35
  }
36
36
 
37
- let path = window.siteVariables.basePath || '/';
37
+ let path = __SITE_BASE_PATH__;
38
38
  if (path.endsWith('/')) {
39
39
  path = path.slice(0, -1);
40
40
  }
@@ -54,7 +54,7 @@
54
54
  <header data-pagefind-ignore>
55
55
  <details>
56
56
  <summary aria-label="Toggle site navigation">
57
- <span class="logo" aria-hidden="true"><%= site.symbol %></span>
57
+ <% if (site.symbol) { %><span class="logo" aria-hidden="true"><%= site.symbol %></span><% } %>
58
58
  <span class="site-title" aria-hidden="true"><%= site.title %></span>
59
59
  <div id="to-top-container" class="to-top-container"></div>
60
60
  <% if (site.features.search) { %>
package/types/dev.d.ts CHANGED
@@ -1,3 +1 @@
1
- interface Window {
2
- IS_DEV: boolean | undefined;
3
- }
1
+ declare const __IS_DEV__: boolean;
@@ -1,16 +1,12 @@
1
- interface TimezoneDef {
1
+ interface TimeZone {
2
2
  value: string;
3
3
  label: string;
4
4
  abbreviation: string;
5
5
  offsetMinutes?: number;
6
6
  }
7
7
 
8
- interface Window {
9
- siteVariables: {
10
- base: string;
11
- basePath: string;
12
- titlePostfix?: string;
13
- defaultTimeZone: string;
14
- timezones: TimezoneDef[];
15
- };
16
- }
8
+ declare const __SITE_BASE__: string;
9
+ declare const __SITE_BASE_PATH__: string;
10
+ declare const __SITE_TITLE_POSTFIX__: string;
11
+ declare const __SITE_DEFAULT_TIMEZONE__: string;
12
+ declare const __SITE_TIMEZONES__: TimeZone[];
@@ -22,6 +22,13 @@ declare module 'pagefind' {
22
22
  export function close(): Promise<void>;
23
23
  }
24
24
 
25
+ declare module 'katex/contrib/render-a11y-string' {
26
+ export default function renderA11yString(
27
+ text: string,
28
+ settings?: Record<string, unknown>,
29
+ ): string;
30
+ }
31
+
25
32
  declare module 'jsdom' {
26
33
  export class JSDOM {
27
34
  constructor(html?: string | Buffer, options?: Record<string, unknown>);
@@ -1 +0,0 @@
1
- { "alex": { "name": "Alex Breen", "avatar": "/avatars/alex.jpg" } }
package/config/nav.json DELETED
@@ -1,28 +0,0 @@
1
- [
2
- {
3
- "title": "Navigation",
4
- "links": [{ "text": "Home", "internal": "/index.html" }]
5
- },
6
- {
7
- "title": "Topics",
8
- "links": [
9
- { "text": "Lectures", "internal": "/lectures/index.html" },
10
- {
11
- "text": "Problem Sets",
12
- "internal": "/problem_sets/index.html",
13
- "disabled": true
14
- }
15
- ]
16
- },
17
- {
18
- "title": "Links",
19
- "links": [
20
- { "text": "Zoom", "external": "https://zoom.com" },
21
- {
22
- "text": "Canvas",
23
- "external": "https://www.instructure.com/",
24
- "disabled": true
25
- }
26
- ]
27
- }
28
- ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes