@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.
- package/README.md +68 -16
- package/bin/tada.ts +289 -24
- package/bin/validators.test.ts +4 -3
- package/bin/validators.ts +4 -2
- package/build/apply-base-path-plugin.ts +24 -13
- package/build/build-manifest.test.ts +349 -0
- package/build/build-manifest.ts +165 -0
- package/build/bundle.ts +19 -20
- package/build/code.test.ts +7 -3
- package/build/content-watch.ts +6 -6
- package/build/copy.ts +2 -2
- package/build/deflist-id-plugin.ts +1 -1
- package/build/external-links-plugin.ts +2 -2
- package/build/features.ts +1 -1
- package/build/generate-content-assets.ts +12 -7
- package/build/generate-favicon.ts +13 -6
- package/build/generate-fonts.ts +2 -2
- package/build/generate-katex-assets.ts +35 -0
- package/build/generate-manifest.ts +2 -2
- package/build/globals.test.ts +9 -11
- package/build/globals.ts +6 -7
- package/build/json-schema.test.ts +1 -1
- package/build/log.test.ts +1 -1
- package/build/log.ts +2 -2
- package/build/markdown-plugins.test.ts +118 -10
- package/build/pagefind.test.ts +2 -2
- package/build/pagefind.ts +5 -5
- package/build/pdf-text.test.ts +52 -0
- package/build/pipeline.ts +42 -15
- package/build/reachability.test.ts +1 -1
- package/build/serve.ts +3 -3
- package/build/site-variables.ts +13 -5
- package/build/templates.ts +7 -6
- package/build/toc-plugin.test.ts +2 -2
- package/build/toc-plugin.ts +1 -1
- package/build/types.ts +15 -6
- package/build/util.ts +6 -6
- package/build/utils/code.ts +5 -5
- package/build/utils/content-files.ts +3 -3
- package/build/utils/derive-theme.test.ts +1 -1
- package/build/utils/derive-theme.ts +1 -1
- package/build/utils/file-types.test.ts +1 -1
- package/build/utils/front-matter.test.ts +1 -4
- package/build/utils/front-matter.ts +2 -2
- package/build/utils/literate-java.ts +11 -10
- package/build/utils/markdown.ts +41 -9
- package/build/utils/paths.test.ts +2 -2
- package/build/utils/paths.ts +5 -1
- package/build/utils/render.ts +45 -32
- package/build/utils/shiki-highlighter.ts +1 -1
- package/build/validate-internal-links-plugin.test.ts +1 -1
- package/build/validate-internal-links-plugin.ts +1 -1
- package/build/watch-reachability-state.test.ts +2 -2
- package/build/watch-reachability-state.ts +1 -1
- package/build/watch.ts +74 -98
- package/{content → init/content}/lectures/index.md +1 -1
- package/{content → init/content}/markdown.md +26 -7
- package/package.json +9 -7
- package/{templates → schema}/nav.schema.json +1 -2
- package/{build → schema}/site.schema.json +1 -2
- package/src/_alerts.scss +4 -0
- package/src/_base.scss +41 -2
- package/src/_layout.scss +37 -0
- package/src/anchor/index.ts +0 -1
- package/src/index.ts +3 -3
- package/src/search/index.test.ts +203 -0
- package/src/search/index.ts +3 -79
- package/src/search/pdf-utils.ts +79 -0
- package/src/search/style.scss +0 -14
- package/src/style.scss +9 -57
- package/src/timezone/index.ts +5 -6
- package/src/util.ts +1 -1
- package/templates/_top.html +1 -1
- package/types/dev.d.ts +1 -3
- package/types/site-variables.d.ts +6 -10
- package/types/untyped-modules.d.ts +7 -0
- package/config/authors.json +0 -1
- package/config/nav.json +0 -28
- /package/{content → init/content}/index.md +0 -0
- /package/{content → init/content}/lectures/01/Pair.java.md +0 -0
- /package/{content → init/content}/lectures/01/Rectangle.java +0 -0
- /package/{content → init/content}/lectures/01/demo.py +0 -0
- /package/{content → init/content}/lectures/01/index.md +0 -0
- /package/{content → init/content}/lectures/01/lecture1.pdf +0 -0
- /package/{content → init/content}/problem_sets/index.html +0 -0
- /package/{public → init/public}/avatars/alex.jpg +0 -0
- /package/{public → init/public}/test.txt +0 -0
- /package/{templates → schema}/authors.schema.json +0 -0
package/build/watch.ts
CHANGED
|
@@ -4,37 +4,33 @@ import { fork } from 'child_process';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import chokidar from 'chokidar';
|
|
6
6
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
7
|
-
import type { SiteVariables, WatchState } from './types
|
|
8
|
-
import { B } from './colors
|
|
9
|
-
import { makeLogger, printFlair } from './log
|
|
10
|
-
import { getDevSiteVariables } from './site-variables
|
|
11
|
-
import {
|
|
12
|
-
compileTemplates,
|
|
13
|
-
getHtmlTemplatesDir,
|
|
14
|
-
getJsonDataDir,
|
|
15
|
-
JSON_DATA_FILES,
|
|
16
|
-
} from './templates.js';
|
|
7
|
+
import type { SiteVariables, WatchState } from './types';
|
|
8
|
+
import { B } from './colors';
|
|
9
|
+
import { makeLogger, printFlair } from './log';
|
|
10
|
+
import { getDevSiteVariables } from './site-variables';
|
|
11
|
+
import { compileTemplates, getJsonDataDir, JSON_DATA_FILES } from './templates';
|
|
17
12
|
import {
|
|
18
13
|
getContentDir,
|
|
19
14
|
getPublicDir,
|
|
20
15
|
getDistDir,
|
|
21
16
|
getPackageDir,
|
|
22
|
-
} from './utils/paths
|
|
23
|
-
import { isFeatureEnabled } from './features
|
|
24
|
-
import { bundle } from './bundle
|
|
25
|
-
import { copyFonts } from './generate-fonts
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
17
|
+
} from './utils/paths';
|
|
18
|
+
import { isFeatureEnabled } from './features';
|
|
19
|
+
import { bundle } from './bundle';
|
|
20
|
+
import { copyFonts } from './generate-fonts';
|
|
21
|
+
import { copyKatexAssets } from './generate-katex-assets';
|
|
22
|
+
import { generateFavicons } from './generate-favicon';
|
|
23
|
+
import { generateManifest } from './generate-manifest';
|
|
28
24
|
import {
|
|
29
25
|
copyPublicFiles,
|
|
30
26
|
copyContentAssets,
|
|
31
27
|
copyContentFile,
|
|
32
28
|
copyPublicFile,
|
|
33
|
-
} from './copy
|
|
34
|
-
import { getProcessedExtensions } from './utils/file-types
|
|
35
|
-
import { ContentRenderer } from './generate-content-assets
|
|
36
|
-
import { WatchPagefindRunner } from './pagefind
|
|
37
|
-
import { ContentChangeDetector } from './content-watch
|
|
29
|
+
} from './copy';
|
|
30
|
+
import { getProcessedExtensions } from './utils/file-types';
|
|
31
|
+
import { ContentRenderer } from './generate-content-assets';
|
|
32
|
+
import { WatchPagefindRunner } from './pagefind';
|
|
33
|
+
import { ContentChangeDetector } from './content-watch';
|
|
38
34
|
|
|
39
35
|
function getArg(name: string): number | null {
|
|
40
36
|
const idx = process.argv.indexOf(name);
|
|
@@ -74,7 +70,7 @@ async function bundleReloadClient(): Promise<string[]> {
|
|
|
74
70
|
);
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
type ChangeCategory = 'content' | 'public' | '
|
|
73
|
+
type ChangeCategory = 'content' | 'public' | 'config';
|
|
78
74
|
|
|
79
75
|
const log = makeLogger(__filename);
|
|
80
76
|
const wslog = makeLogger('WebSocket');
|
|
@@ -82,6 +78,7 @@ const wslog = makeLogger('WebSocket');
|
|
|
82
78
|
// --- WebSocket server (unchanged) ---
|
|
83
79
|
|
|
84
80
|
let webSocketsReady = false;
|
|
81
|
+
let watcherReady = false;
|
|
85
82
|
let webServerReady = false;
|
|
86
83
|
let webServerTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
87
84
|
let serveStarted = false;
|
|
@@ -92,6 +89,12 @@ try {
|
|
|
92
89
|
|
|
93
90
|
wss.on('connection', (conn: WebSocket) => {
|
|
94
91
|
wslog.debug`WebSocket client connected`;
|
|
92
|
+
if (watcherReady && conn.readyState === WebSocket.OPEN) {
|
|
93
|
+
if (initialBuildFailed) {
|
|
94
|
+
conn.send('error');
|
|
95
|
+
}
|
|
96
|
+
conn.send('ready');
|
|
97
|
+
}
|
|
95
98
|
conn.on('close', () => {
|
|
96
99
|
wslog.debug`WebSocket client disconnected`;
|
|
97
100
|
});
|
|
@@ -196,12 +199,9 @@ function toPublicRelativePath(filePath: string): string | null {
|
|
|
196
199
|
const contentDir: string = getContentDir();
|
|
197
200
|
const publicDir: string = getPublicDir();
|
|
198
201
|
const distDir: string = getDistDir();
|
|
199
|
-
const packageDir: string = getPackageDir();
|
|
200
202
|
|
|
201
|
-
let siteVariables: SiteVariables
|
|
202
|
-
let processedExtSet = new Set<string>(
|
|
203
|
-
getProcessedExtensions(Object.keys(siteVariables.codeLanguages || {})),
|
|
204
|
-
);
|
|
203
|
+
let siteVariables: SiteVariables;
|
|
204
|
+
let processedExtSet = new Set<string>();
|
|
205
205
|
let publicRelPaths: Set<string> = new Set();
|
|
206
206
|
let contentAssetRelPaths: Set<string> = new Set();
|
|
207
207
|
let contentRenderer: ContentRenderer;
|
|
@@ -213,6 +213,10 @@ const pendingChanges = new Set<string>();
|
|
|
213
213
|
let rebuilding = false;
|
|
214
214
|
|
|
215
215
|
async function initialBuild(): Promise<void> {
|
|
216
|
+
siteVariables = getDevSiteVariables();
|
|
217
|
+
processedExtSet = new Set(
|
|
218
|
+
getProcessedExtensions(Object.keys(siteVariables.codeLanguages || {})),
|
|
219
|
+
);
|
|
216
220
|
fs.mkdirSync(distDir, { recursive: true });
|
|
217
221
|
|
|
218
222
|
compileTemplates(siteVariables);
|
|
@@ -231,6 +235,7 @@ async function initialBuild(): Promise<void> {
|
|
|
231
235
|
];
|
|
232
236
|
|
|
233
237
|
copyFonts(distDir);
|
|
238
|
+
copyKatexAssets(distDir);
|
|
234
239
|
|
|
235
240
|
if (isFeatureEnabled(siteVariables, 'favicon')) {
|
|
236
241
|
await generateFavicons(siteVariables, distDir);
|
|
@@ -270,8 +275,6 @@ function classifyChange(filePath: string): ChangeCategory | null {
|
|
|
270
275
|
const resolved = path.resolve(filePath);
|
|
271
276
|
const resolvedContentDir = path.resolve(contentDir) + path.sep;
|
|
272
277
|
const resolvedPublicDir = path.resolve(publicDir) + path.sep;
|
|
273
|
-
const resolvedSrcDir = path.resolve(packageDir, 'src') + path.sep;
|
|
274
|
-
const htmlTemplatesDir = path.resolve(getHtmlTemplatesDir()) + path.sep;
|
|
275
278
|
const jsonDataDir = getJsonDataDir();
|
|
276
279
|
const siteConfigPath = path.resolve('site.dev.json');
|
|
277
280
|
|
|
@@ -281,16 +284,10 @@ function classifyChange(filePath: string): ChangeCategory | null {
|
|
|
281
284
|
if (resolved.startsWith(resolvedPublicDir)) {
|
|
282
285
|
return 'public';
|
|
283
286
|
}
|
|
284
|
-
if (resolved.startsWith(resolvedSrcDir)) {
|
|
285
|
-
return 'src';
|
|
286
|
-
}
|
|
287
287
|
if (
|
|
288
|
-
resolved
|
|
288
|
+
resolved === siteConfigPath ||
|
|
289
289
|
JSON_DATA_FILES.some(f => resolved === path.resolve(jsonDataDir, f))
|
|
290
290
|
) {
|
|
291
|
-
return 'templates';
|
|
292
|
-
}
|
|
293
|
-
if (resolved === siteConfigPath) {
|
|
294
291
|
return 'config';
|
|
295
292
|
}
|
|
296
293
|
return null;
|
|
@@ -316,6 +313,8 @@ async function rebuild(): Promise<void> {
|
|
|
316
313
|
}
|
|
317
314
|
}
|
|
318
315
|
|
|
316
|
+
let succeeded = false;
|
|
317
|
+
|
|
319
318
|
try {
|
|
320
319
|
// Site config changed, full restart
|
|
321
320
|
if (categories.has('config')) {
|
|
@@ -337,61 +336,36 @@ async function rebuild(): Promise<void> {
|
|
|
337
336
|
...(await bundleReloadClient()),
|
|
338
337
|
];
|
|
339
338
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
broadcast('reload');
|
|
347
|
-
if (pagefindRunner) {
|
|
348
|
-
pagefindRunner.update(distDir, result.htmlAssetsByPath);
|
|
349
|
-
setImmediate(() => pagefindRunner!.run());
|
|
350
|
-
}
|
|
339
|
+
copyFonts(distDir);
|
|
340
|
+
copyKatexAssets(distDir);
|
|
341
|
+
|
|
342
|
+
if (isFeatureEnabled(siteVariables, 'favicon')) {
|
|
343
|
+
await generateFavicons(siteVariables, distDir);
|
|
344
|
+
generateManifest(siteVariables, distDir);
|
|
351
345
|
}
|
|
352
|
-
rebuilding = false;
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
346
|
|
|
356
|
-
// Source changed, re-bundle, then re-render all content
|
|
357
|
-
if (categories.has('src')) {
|
|
358
|
-
assetFiles = [
|
|
359
|
-
...(await bundle(siteVariables, { mode: 'development' })),
|
|
360
|
-
...(await bundleReloadClient()),
|
|
361
|
-
];
|
|
362
|
-
// Force full content re-render since asset filenames may have changed
|
|
363
347
|
const result = contentRenderer.processContent({ distDir, assetFiles });
|
|
364
348
|
for (const err of result.errors) {
|
|
365
349
|
log.error`${err.message}`;
|
|
366
350
|
}
|
|
367
351
|
if (result.errors.length === 0) {
|
|
368
352
|
printFlair();
|
|
353
|
+
if (!serveStarted) {
|
|
354
|
+
serveStarted = true;
|
|
355
|
+
serve();
|
|
356
|
+
}
|
|
369
357
|
broadcast('reload');
|
|
370
358
|
if (pagefindRunner) {
|
|
371
359
|
pagefindRunner.update(distDir, result.htmlAssetsByPath);
|
|
372
360
|
setImmediate(() => pagefindRunner!.run());
|
|
373
361
|
}
|
|
362
|
+
succeeded = true;
|
|
374
363
|
}
|
|
375
|
-
rebuilding = false;
|
|
376
364
|
return;
|
|
377
365
|
}
|
|
378
366
|
|
|
379
|
-
// Templates/data changed, recompile templates, re-render all content
|
|
380
|
-
if (categories.has('templates')) {
|
|
381
|
-
const detection = changeDetector.detectChanges(changes);
|
|
382
|
-
if (detection.templateError) {
|
|
383
|
-
log.error`Template error: ${detection.templateError.message}`;
|
|
384
|
-
rebuilding = false;
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
367
|
// Public file changed, copy just that file
|
|
390
|
-
if (
|
|
391
|
-
categories.has('public') &&
|
|
392
|
-
!categories.has('content') &&
|
|
393
|
-
!categories.has('templates')
|
|
394
|
-
) {
|
|
368
|
+
if (categories.has('public') && !categories.has('content')) {
|
|
395
369
|
for (const filePath of changes) {
|
|
396
370
|
if (classifyChange(filePath) === 'public') {
|
|
397
371
|
const absPath = path.resolve(filePath);
|
|
@@ -407,17 +381,12 @@ async function rebuild(): Promise<void> {
|
|
|
407
381
|
}
|
|
408
382
|
printFlair();
|
|
409
383
|
broadcast('reload');
|
|
410
|
-
|
|
384
|
+
succeeded = true;
|
|
411
385
|
return;
|
|
412
386
|
}
|
|
413
387
|
|
|
414
|
-
// Content
|
|
388
|
+
// Content changed, incremental rebuild
|
|
415
389
|
const detection = changeDetector.detectChanges(changes);
|
|
416
|
-
if (detection.templateError) {
|
|
417
|
-
log.error`Template error: ${detection.templateError.message}`;
|
|
418
|
-
rebuilding = false;
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
390
|
|
|
422
391
|
if (detection.needsRestart) {
|
|
423
392
|
log.event`Content structure changed, full rebuild`;
|
|
@@ -493,16 +462,24 @@ async function rebuild(): Promise<void> {
|
|
|
493
462
|
|
|
494
463
|
if (result.errors.length === 0) {
|
|
495
464
|
printFlair();
|
|
465
|
+
if (!serveStarted) {
|
|
466
|
+
serveStarted = true;
|
|
467
|
+
serve();
|
|
468
|
+
}
|
|
496
469
|
broadcast('reload');
|
|
497
470
|
|
|
498
471
|
if (pagefindRunner) {
|
|
499
472
|
pagefindRunner.update(distDir, result.htmlAssetsByPath);
|
|
500
473
|
setImmediate(() => pagefindRunner!.run());
|
|
501
474
|
}
|
|
475
|
+
succeeded = true;
|
|
502
476
|
}
|
|
503
477
|
} catch (err) {
|
|
504
478
|
log.error`Build failed: ${(err as Error).message}`;
|
|
505
479
|
} finally {
|
|
480
|
+
if (!succeeded) {
|
|
481
|
+
broadcast('error');
|
|
482
|
+
}
|
|
506
483
|
rebuilding = false;
|
|
507
484
|
|
|
508
485
|
// If more changes accumulated during rebuild, schedule another
|
|
@@ -526,22 +503,18 @@ function onFileChange(filePath: string): void {
|
|
|
526
503
|
|
|
527
504
|
// --- Start ---
|
|
528
505
|
|
|
506
|
+
let initialBuildFailed = false;
|
|
507
|
+
|
|
529
508
|
initialBuild()
|
|
530
|
-
.
|
|
531
|
-
|
|
509
|
+
.catch(err => {
|
|
510
|
+
log.error`Initial build failed: ${(err as Error).message}`;
|
|
511
|
+
initialBuildFailed = true;
|
|
512
|
+
})
|
|
513
|
+
.finally(() => {
|
|
514
|
+
// Watch content, public, data files, site config
|
|
532
515
|
const watchPaths: string[] = [contentDir, publicDir];
|
|
533
516
|
|
|
534
|
-
// Watch
|
|
535
|
-
const srcDir = path.resolve(packageDir, 'src');
|
|
536
|
-
const templatesDir = getHtmlTemplatesDir();
|
|
537
|
-
if (fs.existsSync(srcDir)) {
|
|
538
|
-
watchPaths.push(srcDir);
|
|
539
|
-
}
|
|
540
|
-
if (fs.existsSync(templatesDir)) {
|
|
541
|
-
watchPaths.push(templatesDir);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Watch data files
|
|
517
|
+
// Watch data files (nav.json, authors.json)
|
|
545
518
|
const jsonDataDir = getJsonDataDir();
|
|
546
519
|
for (const dataFile of JSON_DATA_FILES) {
|
|
547
520
|
const dataPath = path.join(jsonDataDir, dataFile);
|
|
@@ -565,9 +538,12 @@ initialBuild()
|
|
|
565
538
|
watcher.on('add', onFileChange);
|
|
566
539
|
watcher.on('unlink', onFileChange);
|
|
567
540
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
541
|
+
watcher.on('ready', () => {
|
|
542
|
+
watcherReady = true;
|
|
543
|
+
if (initialBuildFailed) {
|
|
544
|
+
broadcast('error');
|
|
545
|
+
}
|
|
546
|
+
broadcast('ready');
|
|
547
|
+
log.info`Watching for changes...`;
|
|
548
|
+
});
|
|
573
549
|
});
|
|
@@ -2,7 +2,7 @@ title: Lectures
|
|
|
2
2
|
author: alex
|
|
3
3
|
description: An example page for <%= site.title %>.
|
|
4
4
|
|
|
5
|
-
<%=
|
|
5
|
+
<%= renderTimeZoneChooser() %>
|
|
6
6
|
|
|
7
7
|
One-hour lectures are held weekly at <time datetime="17:40">5:40 PM</time>.
|
|
8
8
|
Each lecture is recorded and made available by password-protected link.
|
|
@@ -128,6 +128,20 @@ See `config/site.dev.json` and `config/site.prod.json` to change `site.basePath`
|
|
|
128
128
|
and `site.internalDomains`.
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
## LaTeX
|
|
132
|
+
|
|
133
|
+
$\LaTeX$ support is implemented via [KaTeX](https://katex.org/). The standard
|
|
134
|
+
dollar sign-based syntax is supported. Wrap inline math in single `$`,
|
|
135
|
+
or use `$$` for a math block.
|
|
136
|
+
|
|
137
|
+
Here's an example: selection sort performs $O(n^2)$ comparisons. And to
|
|
138
|
+
demonstrate a block, here's the exact number of comparisons:
|
|
139
|
+
|
|
140
|
+
$$
|
|
141
|
+
(n-1) + (n-2) + \dots + 1 = \sum_{i=1}^{n-1}i
|
|
142
|
+
$$
|
|
143
|
+
|
|
144
|
+
|
|
131
145
|
## Additional syntax
|
|
132
146
|
|
|
133
147
|
!!! note Headings are links
|
|
@@ -228,7 +242,7 @@ Beware of this rule.
|
|
|
228
242
|
A time zone chooser for testing styles (see the "time zone chooser" section
|
|
229
243
|
below):
|
|
230
244
|
|
|
231
|
-
<%=
|
|
245
|
+
<%= renderTimeZoneChooser() %>
|
|
232
246
|
|
|
233
247
|
A `<time>` element for testing styles: <time datetime="15:00">3 pm</time>
|
|
234
248
|
|
|
@@ -326,14 +340,19 @@ These features aren't Markdown-specific, but are included here for reference.
|
|
|
326
340
|
|
|
327
341
|
Allows the user to see times in their local time zone.
|
|
328
342
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
343
|
+
Insert the time zone chooser using Lodash templates, calling the
|
|
344
|
+
`renderTimeZoneChooser()` function (made available on any page as a global):
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
<%= '\x3C%= renderTimeZoneChooser() %\x3E' %>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
To produce:
|
|
332
351
|
|
|
333
|
-
<%=
|
|
352
|
+
<%= renderTimeZoneChooser() %>
|
|
334
353
|
|
|
335
|
-
|
|
336
|
-
when the user makes a time zone selection.
|
|
354
|
+
Then, wrap your times in `<time datetime="...">` elements and they will be
|
|
355
|
+
updated when the user makes a time zone selection.
|
|
337
356
|
|
|
338
357
|
Use
|
|
339
358
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abreen/tada",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A static site generator",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,24 +13,25 @@
|
|
|
13
13
|
"src/",
|
|
14
14
|
"templates/",
|
|
15
15
|
"fonts/",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"public/",
|
|
16
|
+
"init/",
|
|
17
|
+
"schema/",
|
|
19
18
|
"types/",
|
|
20
19
|
"tsconfig.json"
|
|
21
20
|
],
|
|
22
21
|
"scripts": {
|
|
23
|
-
"prepare": "husky",
|
|
24
|
-
"init-example": "bun run bin/tada.ts init example --
|
|
22
|
+
"prepare": "husky && bun run scripts/check-deps.ts",
|
|
23
|
+
"init-example": "bun run bin/tada.ts init example --no-interactive",
|
|
25
24
|
"lint": "eslint src/ build/ bin/",
|
|
26
25
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"test:functional": "python3 -m pip install -q -r functional_tests/requirements.txt && python3 -m pytest functional_tests/ -v -n auto"
|
|
26
|
+
"test:functional": "python3 -m pip install -q -r functional_tests/requirements.txt && python3 -m pytest functional_tests/ -v -n auto --durations=10",
|
|
27
|
+
"test:all": "bun test && bun run test:functional"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"bun": ">=1.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@tsconfig/node23": "^23.0.2",
|
|
34
|
+
"@vscode/markdown-it-katex": "^1.1.2",
|
|
34
35
|
"ajv": "^8.17.1",
|
|
35
36
|
"chokidar": "^4.0.0",
|
|
36
37
|
"culori": "^4.0.2",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"front-matter": "^4.0.2",
|
|
39
40
|
"java-parser": "^3.0.1",
|
|
40
41
|
"jsdom": "^26.1.0",
|
|
42
|
+
"katex": "^0.16.40",
|
|
41
43
|
"lodash": "^4.17.21",
|
|
42
44
|
"markdown-it": "^14.1.0",
|
|
43
45
|
"markdown-it-anchor": "^9.2.0",
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
"text": { "type": "string" },
|
|
15
15
|
"internal": { "type": "string", "pattern": "^\\/.*$" },
|
|
16
16
|
"external": { "type": "string", "pattern": "^https?:\\/\\/.*$" },
|
|
17
|
-
"disabled": { "type": "boolean" }
|
|
18
|
-
"keywords": { "type": "array", "items": { "type": "string" } }
|
|
17
|
+
"disabled": { "type": "boolean" }
|
|
19
18
|
},
|
|
20
19
|
"oneOf": [{ "required": ["internal"] }, { "required": ["external"] }],
|
|
21
20
|
"additionalProperties": false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "object",
|
|
3
|
-
"required": ["defaultTimeZone"],
|
|
3
|
+
"required": ["base", "title", "defaultTimeZone", "themeColor"],
|
|
4
4
|
"properties": {
|
|
5
5
|
"features": {
|
|
6
6
|
"type": "object",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"title": { "type": "string" },
|
|
28
28
|
"symbol": { "type": "string", "pattern": "^[A-Z0-9\\- ]{1,5}$" },
|
|
29
29
|
"titlePostfix": { "type": "string" },
|
|
30
|
-
"courseId": { "type": "string" },
|
|
31
30
|
"themeColor": { "type": "string" },
|
|
32
31
|
"tintHue": {
|
|
33
32
|
"type": "integer",
|
package/src/_alerts.scss
CHANGED
package/src/_base.scss
CHANGED
|
@@ -74,8 +74,21 @@ html {
|
|
|
74
74
|
a {
|
|
75
75
|
color: var(--fg-color);
|
|
76
76
|
text-decoration-color: var(--fg2-color);
|
|
77
|
-
text-decoration-thickness:
|
|
78
|
-
|
|
77
|
+
text-decoration-thickness: 1.5px;
|
|
78
|
+
|
|
79
|
+
@media (hover: hover) {
|
|
80
|
+
&:hover {
|
|
81
|
+
color: var(--fg2-color);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
a:active {
|
|
87
|
+
color: var(--link-active-color);
|
|
88
|
+
|
|
89
|
+
@media (hover: hover) {
|
|
90
|
+
text-decoration-color: var(--link-active-color);
|
|
91
|
+
}
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
pre,
|
|
@@ -104,3 +117,29 @@ pre {
|
|
|
104
117
|
padding: 0;
|
|
105
118
|
}
|
|
106
119
|
}
|
|
120
|
+
|
|
121
|
+
input[type='text'],
|
|
122
|
+
input[type='search'] {
|
|
123
|
+
background: var(--bg-color);
|
|
124
|
+
color: var(--fg-color);
|
|
125
|
+
border: var(--control-border-width) solid var(--fg2-color);
|
|
126
|
+
border-radius: var(--border-radius);
|
|
127
|
+
font-size: 12pt;
|
|
128
|
+
padding: 4px 4px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
input[type='search'] {
|
|
132
|
+
background-color: var(--bg-color);
|
|
133
|
+
background-image: var(--icon-search);
|
|
134
|
+
background-repeat: no-repeat;
|
|
135
|
+
background-position: left 6px center;
|
|
136
|
+
background-size: 16px 16px;
|
|
137
|
+
padding-left: 2rem;
|
|
138
|
+
border-radius: 18px;
|
|
139
|
+
min-width: 0;
|
|
140
|
+
|
|
141
|
+
&:disabled {
|
|
142
|
+
opacity: 0.4;
|
|
143
|
+
cursor: not-allowed;
|
|
144
|
+
}
|
|
145
|
+
}
|
package/src/_layout.scss
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use 'mixins' as *;
|
|
2
|
+
|
|
1
3
|
:root {
|
|
2
4
|
--toc-width: 260px;
|
|
3
5
|
}
|
|
@@ -8,6 +10,7 @@ html {
|
|
|
8
10
|
overflow-y: auto;
|
|
9
11
|
scroll-padding-top: calc(var(--header-collapsed-height) + var(--gap));
|
|
10
12
|
}
|
|
13
|
+
|
|
11
14
|
body {
|
|
12
15
|
margin: 0;
|
|
13
16
|
padding: 0;
|
|
@@ -132,6 +135,40 @@ body.literate main table {
|
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
137
|
|
|
138
|
+
.info {
|
|
139
|
+
@include user-select(none);
|
|
140
|
+
display: grid;
|
|
141
|
+
grid-template-columns: 1fr auto;
|
|
142
|
+
align-items: baseline;
|
|
143
|
+
color: var(--fg2-color);
|
|
144
|
+
font-size: var(--font-size-smaller);
|
|
145
|
+
|
|
146
|
+
@media (width < 450px) {
|
|
147
|
+
grid-template-columns: 1fr;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
aside.author {
|
|
151
|
+
a {
|
|
152
|
+
text-decoration: none;
|
|
153
|
+
|
|
154
|
+
.text {
|
|
155
|
+
color: var(--fg2-color);
|
|
156
|
+
text-decoration: underline;
|
|
157
|
+
text-decoration-color: var(--fg2-color);
|
|
158
|
+
text-decoration-thickness: 1px;
|
|
159
|
+
text-underline-offset: 3px;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.avatar {
|
|
164
|
+
width: 24px;
|
|
165
|
+
height: 24px;
|
|
166
|
+
vertical-align: middle;
|
|
167
|
+
margin-left: 4px;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
135
172
|
.columns {
|
|
136
173
|
display: grid;
|
|
137
174
|
grid-template-columns: max-content auto;
|
package/src/anchor/index.ts
CHANGED
|
@@ -17,7 +17,6 @@ export default (window: Window) => {
|
|
|
17
17
|
link.className = 'heading-anchor';
|
|
18
18
|
link.href = `#${el.id}`;
|
|
19
19
|
link.title = 'Link to this heading';
|
|
20
|
-
link.setAttribute('aria-label', 'Link to this heading');
|
|
21
20
|
|
|
22
21
|
// Move all existing child nodes into the link
|
|
23
22
|
while (el.firstChild) {
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,7 @@ import mountPrint from './print';
|
|
|
18
18
|
import mountTop from './top';
|
|
19
19
|
import mountAnchor from './anchor';
|
|
20
20
|
import mountQuestion from './question';
|
|
21
|
-
import
|
|
21
|
+
import mountTimeZone from './timezone';
|
|
22
22
|
import mountCode from './code';
|
|
23
23
|
|
|
24
24
|
import { scheduleTask, formatDuration } from './util';
|
|
@@ -31,7 +31,7 @@ const COMPONENTS = {
|
|
|
31
31
|
top: mountTop,
|
|
32
32
|
anchor: mountAnchor,
|
|
33
33
|
question: mountQuestion,
|
|
34
|
-
|
|
34
|
+
timeZone: mountTimeZone,
|
|
35
35
|
code: mountCode,
|
|
36
36
|
};
|
|
37
37
|
|
|
@@ -65,7 +65,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
65
65
|
console.error(`Failed to mount ${name} component:`, reason);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
if (
|
|
68
|
+
if (__IS_DEV__) {
|
|
69
69
|
const diff = window.performance.now() - startTime;
|
|
70
70
|
console.info(`Components mounted in ${formatDuration(diff)}`);
|
|
71
71
|
}
|