mr-md 1.0.4 → 2.0.0-beta
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 +10 -5
- package/dist/builder.d.ts +6 -20
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +38 -97
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +92 -0
- package/dist/cli/generate.d.ts +2 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +171 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +89 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +27 -0
- package/dist/client/app.js +282 -107
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/renderer/blocks.d.ts.map +1 -1
- package/dist/renderer/blocks.js +88 -16
- package/dist/renderer/html-neo.d.ts +7 -0
- package/dist/renderer/html-neo.d.ts.map +1 -0
- package/dist/renderer/html-neo.js +173 -0
- package/dist/renderer/html.d.ts.map +1 -1
- package/dist/renderer/html.js +36 -7
- package/dist/renderer/index-neo.d.ts +4 -0
- package/dist/renderer/index-neo.d.ts.map +1 -0
- package/dist/renderer/index-neo.js +469 -0
- package/dist/renderer/index.d.ts +1 -2
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +29 -379
- package/dist/renderer/markdown.d.ts +1 -1
- package/dist/renderer/markdown.d.ts.map +1 -1
- package/dist/renderer/markdown.js +3 -3
- package/dist/renderer/utils.d.ts +1 -1
- package/dist/renderer/utils.d.ts.map +1 -1
- package/dist/renderer/utils.js +41 -34
- package/dist/styles/theme-neo.css +1369 -0
- package/dist/styles/theme.css +412 -127
- package/dist/types.d.ts +8 -10
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/builder.ts +49 -125
- package/src/cli/dev.ts +102 -0
- package/src/cli/generate.ts +191 -0
- package/src/cli/init.ts +97 -0
- package/src/cli.ts +29 -0
- package/src/client/app.js +282 -107
- package/src/index.ts +1 -1
- package/src/renderer/blocks.ts +89 -15
- package/src/renderer/html.ts +36 -7
- package/src/renderer/index.ts +30 -394
- package/src/renderer/markdown.ts +3 -2
- package/src/renderer/utils.ts +43 -36
- package/src/styles/theme.css +412 -127
- package/src/types.ts +8 -12
package/README.md
CHANGED
|
@@ -13,12 +13,17 @@ The documentation has been split into dedicated guides. Start with the Quick Sta
|
|
|
13
13
|
5. [Quizzes](./docs/quizzes.md) - Learn the JSON format for injecting interactive quizzes.
|
|
14
14
|
6. [Production Checks](./docs/production-checks.md) - Understand Mr Markdown's strict mode and quality enforcement.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Quick Start (CLI)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
The easiest way to use Mr Markdown is via the CLI which provides a smart scaffold and dev server.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
mkdir my-course && cd my-course
|
|
22
|
+
bunx mr-md init
|
|
23
|
+
bunx mr-md g ch physics
|
|
24
|
+
cd chapters/01-physics
|
|
25
|
+
bunx mr-md g lesson intro
|
|
26
|
+
|
|
27
|
+
# Start the dev server
|
|
28
|
+
bunx mr-md dev .
|
|
24
29
|
```
|
package/dist/builder.d.ts
CHANGED
|
@@ -44,9 +44,9 @@ export declare class LessonBuilder {
|
|
|
44
44
|
add(src: `${string}.mp4` | `${string}.webm` | `${string}.mov`, opts?: Omit<MediaOptions, "kind">): this;
|
|
45
45
|
add(src: `${string}.mp3` | `${string}.wav` | `${string}.ogg` | `${string}.m4a`, opts?: Omit<MediaOptions, "kind">): this;
|
|
46
46
|
add(src: `${string}.png` | `${string}.jpg` | `${string}.jpeg` | `${string}.gif` | `${string}.svg` | `${string}.webp` | `${string}.avif`, opts?: Omit<MediaOptions, "kind">): this;
|
|
47
|
-
add(src: string, opts?:
|
|
47
|
+
add(src: string, opts?: unknown): this;
|
|
48
48
|
/**
|
|
49
|
-
* Creates a major chapter heading (
|
|
49
|
+
* Creates a major chapter heading (H1) and a top-level sidebar navigation entry.
|
|
50
50
|
* @param src Path to a markdown file or raw markdown string.
|
|
51
51
|
* @param title Optional title override for the sidebar and heading.
|
|
52
52
|
* @example lesson.heading("# Welcome to Physics")
|
|
@@ -64,10 +64,10 @@ export declare class LessonBuilder {
|
|
|
64
64
|
*/
|
|
65
65
|
content(src: string): this;
|
|
66
66
|
/**
|
|
67
|
-
* Creates a new subsection heading (
|
|
67
|
+
* Creates a new subsection heading (H2) and a sub-entry in the sidebar.
|
|
68
68
|
* @param src Path to a markdown file or raw markdown string.
|
|
69
69
|
* @param label Optional title override for the sidebar label.
|
|
70
|
-
* @example lesson.section("
|
|
70
|
+
* @example lesson.section("## 1. Kinematics")
|
|
71
71
|
*/
|
|
72
72
|
section(src: string, label?: string): this;
|
|
73
73
|
/**
|
|
@@ -141,7 +141,7 @@ export declare class LessonBuilder {
|
|
|
141
141
|
* @param options Build configuration for this specific lesson.
|
|
142
142
|
* @example const l = lesson("Introduction to Kinematics").markdown("intro.md");
|
|
143
143
|
*/
|
|
144
|
-
export declare function lesson(title: string,
|
|
144
|
+
export declare function lesson(title: string, optionsOrSetup?: BuildOptions | ((ctx: LessonBuilder) => void), setup?: (ctx: LessonBuilder) => void): LessonBuilder;
|
|
145
145
|
export declare class ChapterBuilder {
|
|
146
146
|
private meta;
|
|
147
147
|
private lessonBuilders;
|
|
@@ -149,8 +149,6 @@ export declare class ChapterBuilder {
|
|
|
149
149
|
private _rawOptions;
|
|
150
150
|
constructor(title: string, options?: BuildOptions, callerDir?: string);
|
|
151
151
|
slug(slug: string): this;
|
|
152
|
-
/** @internal Used by CourseBuilder to push down shared config */
|
|
153
|
-
_inheritOptions(parentOpts: BuildOptions, parentRawOpts?: BuildOptions): void;
|
|
154
152
|
description(text: string): this;
|
|
155
153
|
status(status: "completed" | "active" | "locked"): this;
|
|
156
154
|
lesson(lessonBuilder: LessonBuilder): this;
|
|
@@ -168,17 +166,5 @@ export declare class ChapterBuilder {
|
|
|
168
166
|
* .lesson(dynamicsLesson)
|
|
169
167
|
* .build();
|
|
170
168
|
*/
|
|
171
|
-
export declare function chapter(title: string,
|
|
172
|
-
export declare class CourseBuilder {
|
|
173
|
-
private meta;
|
|
174
|
-
private chapterBuilders;
|
|
175
|
-
private options;
|
|
176
|
-
private _rawOptions;
|
|
177
|
-
constructor(title: string, options?: BuildOptions, callerDir?: string);
|
|
178
|
-
slug(slug: string): this;
|
|
179
|
-
description(text: string): this;
|
|
180
|
-
chapter(chapterBuilder: ChapterBuilder): this;
|
|
181
|
-
build(): string;
|
|
182
|
-
}
|
|
183
|
-
export declare function course(title: string, options?: BuildOptions): CourseBuilder;
|
|
169
|
+
export declare function chapter(title: string, optionsOrSetup?: BuildOptions | ((ctx: ChapterBuilder) => void), setup?: (ctx: ChapterBuilder) => void): ChapterBuilder;
|
|
184
170
|
//# sourceMappingURL=builder.d.ts.map
|
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEX,gBAAgB,EAEhB,YAAY,EACZ,YAAY,EACZ,OAAO,EAGP,UAAU,EAEV,cAAc,EAId,YAAY,EACZ,MAAM,EACN,UAAU,EAGV,YAAY,EACZ,SAAS,EAIT,iBAAiB,EAEjB,cAAc,EACd,MAAM,YAAY,CAAC;AAsCpB,qBAAa,aAAa;IACzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAe;gBAEtB,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,EAAE,SAAS,CAAC,EAAE,MAAM;IA+BzE;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxB;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK/B,2CAA2C;IAC3C,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAK7B,4CAA4C;IAC5C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK1B;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI;IAKlD,kEAAkE;IAClE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IASzD,kEAAkE;IAClE,eAAe,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/builder.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import { render, renderChapter
|
|
3
|
+
import { render, renderChapter } from "./renderer/index.js";
|
|
4
4
|
function getCallerDir() {
|
|
5
5
|
const err = new Error();
|
|
6
6
|
const stack = err.stack?.split("\n");
|
|
@@ -49,11 +49,11 @@ export class LessonBuilder {
|
|
|
49
49
|
slug,
|
|
50
50
|
};
|
|
51
51
|
this.options = {
|
|
52
|
-
outDir: options.outDir ?? "./out",
|
|
52
|
+
outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : "./out"),
|
|
53
53
|
contentBase: options.contentBase ?? callerDir ?? ".",
|
|
54
54
|
theme: options.theme ?? "auto",
|
|
55
55
|
palette: options.palette ?? "ink",
|
|
56
|
-
strict: options.strict ??
|
|
56
|
+
strict: options.strict ?? process.env.NODE_ENV !== "development",
|
|
57
57
|
preset: {
|
|
58
58
|
layout: "lesson",
|
|
59
59
|
density: "comfortable",
|
|
@@ -109,16 +109,17 @@ export class LessonBuilder {
|
|
|
109
109
|
/** @internal Used by ChapterBuilder to push down shared config */
|
|
110
110
|
_inheritOptions(parentOpts, parentRawOpts) {
|
|
111
111
|
this.options = {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
outDir: this._rawOptions.outDir ?? (parentRawOpts?.outDir || parentOpts.outDir) ?? this.options.outDir,
|
|
113
|
+
contentBase: this._rawOptions.contentBase ??
|
|
114
|
+
parentRawOpts?.contentBase ??
|
|
115
|
+
this.options.contentBase,
|
|
115
116
|
theme: this._rawOptions.theme ?? parentOpts.theme ?? this.options.theme,
|
|
116
117
|
palette: this._rawOptions.palette ?? parentOpts.palette ?? this.options.palette,
|
|
117
118
|
strict: this._rawOptions.strict ?? parentOpts.strict ?? this.options.strict,
|
|
118
119
|
preset: {
|
|
119
|
-
...this.options.preset,
|
|
120
120
|
...parentOpts.preset,
|
|
121
121
|
...this._rawOptions.preset,
|
|
122
|
+
...this.options.preset,
|
|
122
123
|
},
|
|
123
124
|
};
|
|
124
125
|
}
|
|
@@ -140,7 +141,6 @@ export class LessonBuilder {
|
|
|
140
141
|
_getMeta() {
|
|
141
142
|
return this.meta;
|
|
142
143
|
}
|
|
143
|
-
// biome-ignore lint/suspicious/noExplicitAny: Overload implementation
|
|
144
144
|
add(src, opts = {}) {
|
|
145
145
|
const lower = src.toLowerCase();
|
|
146
146
|
if (lower.endsWith(".md") || lower.endsWith(".mdx"))
|
|
@@ -164,7 +164,7 @@ export class LessonBuilder {
|
|
|
164
164
|
return this.markdown(src);
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
|
-
* Creates a major chapter heading (
|
|
167
|
+
* Creates a major chapter heading (H1) and a top-level sidebar navigation entry.
|
|
168
168
|
* @param src Path to a markdown file or raw markdown string.
|
|
169
169
|
* @param title Optional title override for the sidebar and heading.
|
|
170
170
|
* @example lesson.heading("# Welcome to Physics")
|
|
@@ -190,10 +190,10 @@ export class LessonBuilder {
|
|
|
190
190
|
return this.markdown(src);
|
|
191
191
|
}
|
|
192
192
|
/**
|
|
193
|
-
* Creates a new subsection heading (
|
|
193
|
+
* Creates a new subsection heading (H2) and a sub-entry in the sidebar.
|
|
194
194
|
* @param src Path to a markdown file or raw markdown string.
|
|
195
195
|
* @param label Optional title override for the sidebar label.
|
|
196
|
-
* @example lesson.section("
|
|
196
|
+
* @example lesson.section("## 1. Kinematics")
|
|
197
197
|
*/
|
|
198
198
|
section(src, label) {
|
|
199
199
|
this.blocks.push({ type: "section", src, label });
|
|
@@ -389,8 +389,20 @@ export class LessonBuilder {
|
|
|
389
389
|
* @param options Build configuration for this specific lesson.
|
|
390
390
|
* @example const l = lesson("Introduction to Kinematics").markdown("intro.md");
|
|
391
391
|
*/
|
|
392
|
-
export function lesson(title,
|
|
393
|
-
|
|
392
|
+
export function lesson(title, optionsOrSetup, setup) {
|
|
393
|
+
let options = {};
|
|
394
|
+
let cb = setup;
|
|
395
|
+
if (typeof optionsOrSetup === "function") {
|
|
396
|
+
cb = optionsOrSetup;
|
|
397
|
+
}
|
|
398
|
+
else if (optionsOrSetup) {
|
|
399
|
+
options = optionsOrSetup;
|
|
400
|
+
}
|
|
401
|
+
const builder = new LessonBuilder(title, options, getCallerDir());
|
|
402
|
+
if (cb) {
|
|
403
|
+
cb(builder);
|
|
404
|
+
}
|
|
405
|
+
return builder;
|
|
394
406
|
}
|
|
395
407
|
function normalizeSimulationOptions(opts, legacyHeight, fileConfig = null) {
|
|
396
408
|
let inline;
|
|
@@ -493,11 +505,11 @@ export class ChapterBuilder {
|
|
|
493
505
|
slug,
|
|
494
506
|
};
|
|
495
507
|
this.options = {
|
|
496
|
-
outDir: options.outDir ?? "./out",
|
|
508
|
+
outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : "./out"),
|
|
497
509
|
contentBase: options.contentBase ?? callerDir ?? ".",
|
|
498
510
|
theme: options.theme ?? "auto",
|
|
499
511
|
palette: options.palette ?? "ink",
|
|
500
|
-
strict: options.strict ??
|
|
512
|
+
strict: options.strict ?? process.env.NODE_ENV !== "development",
|
|
501
513
|
preset: {
|
|
502
514
|
layout: "lesson",
|
|
503
515
|
density: "comfortable",
|
|
@@ -511,25 +523,6 @@ export class ChapterBuilder {
|
|
|
511
523
|
this.meta.slug = slug;
|
|
512
524
|
return this;
|
|
513
525
|
}
|
|
514
|
-
/** @internal Used by CourseBuilder to push down shared config */
|
|
515
|
-
_inheritOptions(parentOpts, parentRawOpts) {
|
|
516
|
-
this.options = {
|
|
517
|
-
...this.options,
|
|
518
|
-
outDir: this._rawOptions.outDir ?? parentOpts.outDir ?? this.options.outDir,
|
|
519
|
-
contentBase: this._rawOptions.contentBase ?? this.options.contentBase,
|
|
520
|
-
theme: this._rawOptions.theme ?? parentOpts.theme ?? this.options.theme,
|
|
521
|
-
palette: this._rawOptions.palette ?? parentOpts.palette ?? this.options.palette,
|
|
522
|
-
strict: this._rawOptions.strict ?? parentOpts.strict ?? this.options.strict,
|
|
523
|
-
preset: {
|
|
524
|
-
...this.options.preset,
|
|
525
|
-
...parentOpts.preset,
|
|
526
|
-
...this._rawOptions.preset,
|
|
527
|
-
},
|
|
528
|
-
};
|
|
529
|
-
for (const lb of this.lessonBuilders) {
|
|
530
|
-
lb._inheritOptions(this.options, this._rawOptions);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
526
|
description(text) {
|
|
534
527
|
this.meta.description = text;
|
|
535
528
|
return this;
|
|
@@ -595,70 +588,18 @@ export class ChapterBuilder {
|
|
|
595
588
|
* .lesson(dynamicsLesson)
|
|
596
589
|
* .build();
|
|
597
590
|
*/
|
|
598
|
-
export function chapter(title,
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
meta;
|
|
604
|
-
chapterBuilders = [];
|
|
605
|
-
options;
|
|
606
|
-
_rawOptions;
|
|
607
|
-
constructor(title, options = {}, callerDir) {
|
|
608
|
-
this._rawOptions = options;
|
|
609
|
-
let slug = title
|
|
610
|
-
.toLowerCase()
|
|
611
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
612
|
-
.replace(/(^-|-$)/g, "");
|
|
613
|
-
if (!slug)
|
|
614
|
-
slug = "course";
|
|
615
|
-
this.meta = { title, slug };
|
|
616
|
-
this.options = {
|
|
617
|
-
outDir: options.outDir ?? "./out",
|
|
618
|
-
contentBase: options.contentBase ?? callerDir ?? ".",
|
|
619
|
-
theme: options.theme ?? "auto",
|
|
620
|
-
palette: options.palette ?? "ink",
|
|
621
|
-
strict: options.strict ?? true,
|
|
622
|
-
preset: {
|
|
623
|
-
layout: "lesson",
|
|
624
|
-
density: "comfortable",
|
|
625
|
-
tone: "scholarly",
|
|
626
|
-
...options.preset,
|
|
627
|
-
},
|
|
628
|
-
...options,
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
slug(slug) {
|
|
632
|
-
this.meta.slug = slug;
|
|
633
|
-
return this;
|
|
591
|
+
export function chapter(title, optionsOrSetup, setup) {
|
|
592
|
+
let options = {};
|
|
593
|
+
let cb = setup;
|
|
594
|
+
if (typeof optionsOrSetup === "function") {
|
|
595
|
+
cb = optionsOrSetup;
|
|
634
596
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
return this;
|
|
597
|
+
else if (optionsOrSetup) {
|
|
598
|
+
options = optionsOrSetup;
|
|
638
599
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
return this;
|
|
600
|
+
const builder = new ChapterBuilder(title, options, getCallerDir());
|
|
601
|
+
if (cb) {
|
|
602
|
+
cb(builder);
|
|
643
603
|
}
|
|
644
|
-
|
|
645
|
-
const chapters = [];
|
|
646
|
-
for (const cb of this.chapterBuilders) {
|
|
647
|
-
cb.build();
|
|
648
|
-
chapters.push(cb.toJSON());
|
|
649
|
-
}
|
|
650
|
-
const courseData = { meta: this.meta, chapters };
|
|
651
|
-
const html = renderCourse(courseData, this.options);
|
|
652
|
-
const outDir = path.resolve(this.options.outDir);
|
|
653
|
-
if (!fs.existsSync(outDir))
|
|
654
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
655
|
-
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
656
|
-
fs.writeFileSync(outPath, html, "utf-8");
|
|
657
|
-
const relPath = path.relative(process.cwd(), outPath);
|
|
658
|
-
console.log(` ✓ Built course (${this.chapterBuilders.length} chapters) → ${relPath}`);
|
|
659
|
-
return outPath;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
export function course(title, options = {}) {
|
|
663
|
-
return new CourseBuilder(title, options, getCallerDir());
|
|
604
|
+
return builder;
|
|
664
605
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,iBA+F1C"}
|
package/dist/cli/dev.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { exec } from "child_process";
|
|
4
|
+
export async function runDev(args) {
|
|
5
|
+
const dir = args[0] || ".";
|
|
6
|
+
const outDir = path.resolve(process.cwd(), dir, "out");
|
|
7
|
+
console.log(`Starting dev server for directory: ${dir}`);
|
|
8
|
+
let server;
|
|
9
|
+
const rebuild = () => {
|
|
10
|
+
console.log("Rebuilding...");
|
|
11
|
+
const entryPoints = ["chapter.ts", "index.ts", "lesson.ts"];
|
|
12
|
+
for (const entry of entryPoints) {
|
|
13
|
+
const entryPath = path.join(dir, entry);
|
|
14
|
+
if (fs.existsSync(entryPath)) {
|
|
15
|
+
exec(`NODE_ENV=development bun ${entryPath}`, (err, stdout, stderr) => {
|
|
16
|
+
if (err)
|
|
17
|
+
console.error("Build failed:", stderr);
|
|
18
|
+
else {
|
|
19
|
+
console.log("Build successful.");
|
|
20
|
+
server?.publish("livereload", "reload");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
console.log("No chapter.ts or lesson.ts found to build automatically.");
|
|
27
|
+
};
|
|
28
|
+
rebuild();
|
|
29
|
+
if (fs.existsSync(dir)) {
|
|
30
|
+
let timeout;
|
|
31
|
+
fs.watch(dir, { recursive: true }, (eventType, filename) => {
|
|
32
|
+
if (!filename || filename.includes("out/") || filename.includes(".git/"))
|
|
33
|
+
return;
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
timeout = setTimeout(() => {
|
|
36
|
+
console.log(`File changed: ${filename}`);
|
|
37
|
+
rebuild();
|
|
38
|
+
}, 200);
|
|
39
|
+
});
|
|
40
|
+
console.log(`Watching ${dir} for changes...`);
|
|
41
|
+
}
|
|
42
|
+
server = Bun.serve({
|
|
43
|
+
port: 3000,
|
|
44
|
+
async fetch(req, srv) {
|
|
45
|
+
if (srv.upgrade(req))
|
|
46
|
+
return;
|
|
47
|
+
const url = new URL(req.url);
|
|
48
|
+
let filePath = path.join(outDir, url.pathname);
|
|
49
|
+
if (filePath.endsWith("/")) {
|
|
50
|
+
const files = fs.existsSync(outDir) ? fs.readdirSync(outDir) : [];
|
|
51
|
+
const htmlFiles = files.filter(f => f.endsWith(".html"));
|
|
52
|
+
if (htmlFiles.includes("index.html")) {
|
|
53
|
+
filePath = path.join(outDir, "index.html");
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const chapterFile = htmlFiles.find(f => f.includes("chapter"));
|
|
57
|
+
if (chapterFile) {
|
|
58
|
+
filePath = path.join(outDir, chapterFile);
|
|
59
|
+
}
|
|
60
|
+
else if (htmlFiles.length > 0) {
|
|
61
|
+
filePath = path.join(outDir, htmlFiles[0]);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
filePath += "index.html";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
69
|
+
if (filePath.endsWith(".html")) {
|
|
70
|
+
const file = Bun.file(filePath);
|
|
71
|
+
let text = await file.text();
|
|
72
|
+
text = text.replace("</body>", `<script>
|
|
73
|
+
const ws = new WebSocket("ws://localhost:3000/");
|
|
74
|
+
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
75
|
+
</script></body>`);
|
|
76
|
+
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
77
|
+
}
|
|
78
|
+
return new Response(Bun.file(filePath));
|
|
79
|
+
}
|
|
80
|
+
const srcPath = path.join(process.cwd(), dir, url.pathname);
|
|
81
|
+
if (fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
|
|
82
|
+
return new Response(Bun.file(srcPath));
|
|
83
|
+
}
|
|
84
|
+
return new Response("Not found", { status: 404 });
|
|
85
|
+
},
|
|
86
|
+
websocket: {
|
|
87
|
+
message() { },
|
|
88
|
+
open(ws) { ws.subscribe("livereload"); }
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
console.log(`Dev server listening on http://localhost:3000`);
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAyBA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,iBAqK/C"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
function getNextPrefix(dirPath) {
|
|
4
|
+
if (!fs.existsSync(dirPath))
|
|
5
|
+
return "01";
|
|
6
|
+
const files = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
7
|
+
let maxPrefix = 0;
|
|
8
|
+
for (const file of files) {
|
|
9
|
+
if (file.isDirectory()) {
|
|
10
|
+
const match = file.name.match(/^(\d+)-/);
|
|
11
|
+
if (match) {
|
|
12
|
+
const num = parseInt(match[1], 10);
|
|
13
|
+
if (num > maxPrefix) {
|
|
14
|
+
maxPrefix = num;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const next = maxPrefix + 1;
|
|
20
|
+
return next.toString().padStart(2, "0");
|
|
21
|
+
}
|
|
22
|
+
export async function runGenerate(args) {
|
|
23
|
+
const type = args[0];
|
|
24
|
+
const rawName = args[1];
|
|
25
|
+
if (!type || !rawName) {
|
|
26
|
+
console.error("Usage: md g <ch|le|qu|chapter|lesson|quiz> <name>");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
switch (type) {
|
|
32
|
+
case "ch":
|
|
33
|
+
case "chapter": {
|
|
34
|
+
const chaptersDir = path.join(cwd, "chapters");
|
|
35
|
+
if (!fs.existsSync(chaptersDir))
|
|
36
|
+
fs.mkdirSync(chaptersDir, { recursive: true });
|
|
37
|
+
const prefix = getNextPrefix(chaptersDir);
|
|
38
|
+
const chapterDirName = `${prefix}-${name}`;
|
|
39
|
+
const chapterPath = path.join(chaptersDir, chapterDirName);
|
|
40
|
+
fs.mkdirSync(chapterPath, { recursive: true });
|
|
41
|
+
fs.mkdirSync(path.join(chapterPath, "lessons"), { recursive: true });
|
|
42
|
+
const varName = name.replace(/-([a-z0-9])/g, g => g[1].toUpperCase());
|
|
43
|
+
const chapterTitle = rawName.replace(/-/g, " ").replace(/\b\w/g, l => l.toUpperCase());
|
|
44
|
+
const content = `import { chapter } from "mr-md";
|
|
45
|
+
|
|
46
|
+
export const ${varName}Chapter = chapter("${chapterTitle}", ctx => {
|
|
47
|
+
// Add lessons here
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (import.meta.main) {
|
|
51
|
+
${varName}Chapter.build();
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
fs.writeFileSync(path.join(chapterPath, "chapter.ts"), content, "utf-8");
|
|
55
|
+
console.log(`Generated Chapter: chapters/${chapterDirName}`);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "le":
|
|
59
|
+
case "lesson": {
|
|
60
|
+
// Assume we are in a chapter directory
|
|
61
|
+
const lessonsDir = path.join(cwd, "lessons");
|
|
62
|
+
if (!fs.existsSync(lessonsDir))
|
|
63
|
+
fs.mkdirSync(lessonsDir, { recursive: true });
|
|
64
|
+
const prefix = getNextPrefix(lessonsDir);
|
|
65
|
+
const lessonDirName = `${prefix}-${name}`;
|
|
66
|
+
const lessonPath = path.join(lessonsDir, lessonDirName);
|
|
67
|
+
fs.mkdirSync(lessonPath, { recursive: true });
|
|
68
|
+
fs.mkdirSync(path.join(lessonPath, "sims"), { recursive: true });
|
|
69
|
+
fs.mkdirSync(path.join(lessonPath, "media"), { recursive: true });
|
|
70
|
+
fs.mkdirSync(path.join(lessonPath, "quizzes"), { recursive: true });
|
|
71
|
+
fs.mkdirSync(path.join(lessonPath, "content"), { recursive: true });
|
|
72
|
+
const lessonTitle = rawName.replace(/-/g, " ").replace(/\b\w/g, l => l.toUpperCase());
|
|
73
|
+
const varName = name.replace(/-([a-z0-9])/g, g => g[1].toUpperCase());
|
|
74
|
+
const content = `import { lesson } from "mr-md";
|
|
75
|
+
|
|
76
|
+
export const ${varName}Lesson = lesson("${lessonTitle}", { contentBase: import.meta.dir }, ctx => {
|
|
77
|
+
});
|
|
78
|
+
`;
|
|
79
|
+
fs.writeFileSync(path.join(lessonPath, "lesson.ts"), content, "utf-8");
|
|
80
|
+
console.log(`Generated Lesson: lessons/${lessonDirName}`);
|
|
81
|
+
// Auto import into chapter.ts
|
|
82
|
+
const chapterFile = path.join(cwd, "chapter.ts");
|
|
83
|
+
if (fs.existsSync(chapterFile)) {
|
|
84
|
+
const { Project, SyntaxKind } = await import("ts-morph");
|
|
85
|
+
const project = new Project();
|
|
86
|
+
const sourceFile = project.addSourceFileAtPath(chapterFile);
|
|
87
|
+
// 1. Add the import
|
|
88
|
+
sourceFile.addImportDeclaration({
|
|
89
|
+
namedImports: [`${varName}Lesson`],
|
|
90
|
+
moduleSpecifier: `./lessons/${lessonDirName}/lesson.js`
|
|
91
|
+
});
|
|
92
|
+
// 2. Find the chapter(...) call and inject ctx.lesson()
|
|
93
|
+
const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
94
|
+
const chapterCall = calls.find((c) => c.getExpression().getText() === "chapter");
|
|
95
|
+
if (chapterCall) {
|
|
96
|
+
const args = chapterCall.getArguments();
|
|
97
|
+
const callback = args.find((a) => a.getKind() === SyntaxKind.ArrowFunction || a.getKind() === SyntaxKind.FunctionExpression);
|
|
98
|
+
if (callback) {
|
|
99
|
+
const arrow = callback.asKind(SyntaxKind.ArrowFunction);
|
|
100
|
+
const func = callback.asKind(SyntaxKind.FunctionExpression);
|
|
101
|
+
const body = arrow ? arrow.getBody() : func ? func.getBody() : null;
|
|
102
|
+
if (body && body.getKind() === SyntaxKind.Block) {
|
|
103
|
+
body.asKind(SyntaxKind.Block)?.addStatements(`ctx.lesson(${varName}Lesson);`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.warn(" ⚠ Could not auto-import: chapter callback must use { } block syntax.");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.warn(" ⚠ Could not auto-import: could not find chapter callback.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
sourceFile.saveSync();
|
|
114
|
+
console.log(`Auto-imported ${varName}Lesson into chapter.ts`);
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "qu":
|
|
119
|
+
case "quiz": {
|
|
120
|
+
// Assume we are in a lesson directory
|
|
121
|
+
const quizzesDir = path.join(cwd, "quizzes");
|
|
122
|
+
if (!fs.existsSync(quizzesDir))
|
|
123
|
+
fs.mkdirSync(quizzesDir, { recursive: true });
|
|
124
|
+
const quizPath = path.join(quizzesDir, `${name}.json`);
|
|
125
|
+
const content = `{
|
|
126
|
+
"questions": [
|
|
127
|
+
{
|
|
128
|
+
"q": "Sample question?",
|
|
129
|
+
"options": ["A", "B"],
|
|
130
|
+
"answer": 0
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
fs.writeFileSync(quizPath, content, "utf-8");
|
|
136
|
+
console.log(`Generated Quiz: quizzes/${name}.json`);
|
|
137
|
+
// Auto import into lesson.ts
|
|
138
|
+
const lessonFile = path.join(cwd, "lesson.ts");
|
|
139
|
+
if (fs.existsSync(lessonFile)) {
|
|
140
|
+
const { Project, SyntaxKind } = await import("ts-morph");
|
|
141
|
+
const project = new Project();
|
|
142
|
+
const sourceFile = project.addSourceFileAtPath(lessonFile);
|
|
143
|
+
const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
144
|
+
const lessonCall = calls.find((c) => c.getExpression().getText() === "lesson");
|
|
145
|
+
if (lessonCall) {
|
|
146
|
+
const args = lessonCall.getArguments();
|
|
147
|
+
const callback = args.find((a) => a.getKind() === SyntaxKind.ArrowFunction || a.getKind() === SyntaxKind.FunctionExpression);
|
|
148
|
+
if (callback) {
|
|
149
|
+
const arrow = callback.asKind(SyntaxKind.ArrowFunction);
|
|
150
|
+
const func = callback.asKind(SyntaxKind.FunctionExpression);
|
|
151
|
+
const body = arrow ? arrow.getBody() : func ? func.getBody() : null;
|
|
152
|
+
if (body && body.getKind() === SyntaxKind.Block) {
|
|
153
|
+
body.asKind(SyntaxKind.Block)?.addStatements(`ctx.quiz("quizzes/${name}.json");`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
console.warn(" ⚠ Could not auto-import quiz: lesson callback must use { } block syntax.");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.warn(" ⚠ Could not auto-import quiz: could not find lesson callback.");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
sourceFile.saveSync();
|
|
164
|
+
console.log(`Auto-added quiz to lesson.ts`);
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
default:
|
|
169
|
+
console.error(`Unknown generator type: ${type}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAGA,wBAAsB,OAAO,kBA6F5B"}
|