@cuppacue/cli 0.1.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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/ai/__tests__/generate.test.d.ts +2 -0
- package/dist/ai/__tests__/generate.test.d.ts.map +1 -0
- package/dist/ai/__tests__/generate.test.js +129 -0
- package/dist/ai/__tests__/generate.test.js.map +1 -0
- package/dist/ai/__tests__/images.test.d.ts +2 -0
- package/dist/ai/__tests__/images.test.d.ts.map +1 -0
- package/dist/ai/__tests__/images.test.js +186 -0
- package/dist/ai/__tests__/images.test.js.map +1 -0
- package/dist/ai/__tests__/prompt.test.d.ts +2 -0
- package/dist/ai/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/ai/__tests__/prompt.test.js +98 -0
- package/dist/ai/__tests__/prompt.test.js.map +1 -0
- package/dist/ai/__tests__/refine.test.d.ts +2 -0
- package/dist/ai/__tests__/refine.test.d.ts.map +1 -0
- package/dist/ai/__tests__/refine.test.js +87 -0
- package/dist/ai/__tests__/refine.test.js.map +1 -0
- package/dist/ai/client.d.ts +4 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +36 -0
- package/dist/ai/client.js.map +1 -0
- package/dist/ai/example.d.ts +6 -0
- package/dist/ai/example.d.ts.map +1 -0
- package/dist/ai/example.js +284 -0
- package/dist/ai/example.js.map +1 -0
- package/dist/ai/generate.d.ts +15 -0
- package/dist/ai/generate.d.ts.map +1 -0
- package/dist/ai/generate.js +120 -0
- package/dist/ai/generate.js.map +1 -0
- package/dist/ai/images.d.ts +8 -0
- package/dist/ai/images.d.ts.map +1 -0
- package/dist/ai/images.js +71 -0
- package/dist/ai/images.js.map +1 -0
- package/dist/ai/index.d.ts +7 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +7 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/prompt.d.ts +10 -0
- package/dist/ai/prompt.d.ts.map +1 -0
- package/dist/ai/prompt.js +119 -0
- package/dist/ai/prompt.js.map +1 -0
- package/dist/ai/refine.d.ts +6 -0
- package/dist/ai/refine.d.ts.map +1 -0
- package/dist/ai/refine.js +22 -0
- package/dist/ai/refine.js.map +1 -0
- package/dist/ai/schema.d.ts +5 -0
- package/dist/ai/schema.d.ts.map +1 -0
- package/dist/ai/schema.js +292 -0
- package/dist/ai/schema.js.map +1 -0
- package/dist/commands/__tests__/backstage.test.d.ts +2 -0
- package/dist/commands/__tests__/backstage.test.d.ts.map +1 -0
- package/dist/commands/__tests__/backstage.test.js +45 -0
- package/dist/commands/__tests__/backstage.test.js.map +1 -0
- package/dist/commands/__tests__/export.test.d.ts +2 -0
- package/dist/commands/__tests__/export.test.d.ts.map +1 -0
- package/dist/commands/__tests__/export.test.js +50 -0
- package/dist/commands/__tests__/export.test.js.map +1 -0
- package/dist/commands/ai.d.ts +2 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +113 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +121 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/export-pdf.d.ts +9 -0
- package/dist/commands/export-pdf.d.ts.map +1 -0
- package/dist/commands/export-pdf.js +109 -0
- package/dist/commands/export-pdf.js.map +1 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +147 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +54 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/preview.d.ts +2 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +78 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/serve.d.ts +7 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +773 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +39 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/github/__tests__/fetcher.test.d.ts +2 -0
- package/dist/github/__tests__/fetcher.test.d.ts.map +1 -0
- package/dist/github/__tests__/fetcher.test.js +102 -0
- package/dist/github/__tests__/fetcher.test.js.map +1 -0
- package/dist/github/__tests__/rewrite-images.test.d.ts +2 -0
- package/dist/github/__tests__/rewrite-images.test.d.ts.map +1 -0
- package/dist/github/__tests__/rewrite-images.test.js +79 -0
- package/dist/github/__tests__/rewrite-images.test.js.map +1 -0
- package/dist/github/__tests__/url-parser.test.d.ts +2 -0
- package/dist/github/__tests__/url-parser.test.d.ts.map +1 -0
- package/dist/github/__tests__/url-parser.test.js +96 -0
- package/dist/github/__tests__/url-parser.test.js.map +1 -0
- package/dist/github/fetcher.d.ts +10 -0
- package/dist/github/fetcher.d.ts.map +1 -0
- package/dist/github/fetcher.js +53 -0
- package/dist/github/fetcher.js.map +1 -0
- package/dist/github/index.d.ts +5 -0
- package/dist/github/index.d.ts.map +1 -0
- package/dist/github/index.js +4 -0
- package/dist/github/index.js.map +1 -0
- package/dist/github/rewrite-images.d.ts +7 -0
- package/dist/github/rewrite-images.d.ts.map +1 -0
- package/dist/github/rewrite-images.js +26 -0
- package/dist/github/rewrite-images.js.map +1 -0
- package/dist/github/url-parser.d.ts +12 -0
- package/dist/github/url-parser.d.ts.map +1 -0
- package/dist/github/url-parser.js +60 -0
- package/dist/github/url-parser.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/__tests__/auto-slide.test.d.ts +2 -0
- package/dist/markdown/__tests__/auto-slide.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/auto-slide.test.js +325 -0
- package/dist/markdown/__tests__/auto-slide.test.js.map +1 -0
- package/dist/markdown/__tests__/compiler.test.d.ts +2 -0
- package/dist/markdown/__tests__/compiler.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/compiler.test.js +142 -0
- package/dist/markdown/__tests__/compiler.test.js.map +1 -0
- package/dist/markdown/__tests__/custom-parsers.test.d.ts +2 -0
- package/dist/markdown/__tests__/custom-parsers.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/custom-parsers.test.js +182 -0
- package/dist/markdown/__tests__/custom-parsers.test.js.map +1 -0
- package/dist/markdown/__tests__/frontmatter.test.d.ts +2 -0
- package/dist/markdown/__tests__/frontmatter.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/frontmatter.test.js +162 -0
- package/dist/markdown/__tests__/frontmatter.test.js.map +1 -0
- package/dist/markdown/__tests__/layout.test.d.ts +2 -0
- package/dist/markdown/__tests__/layout.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/layout.test.js +85 -0
- package/dist/markdown/__tests__/layout.test.js.map +1 -0
- package/dist/markdown/__tests__/parser.test.d.ts +2 -0
- package/dist/markdown/__tests__/parser.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/parser.test.js +646 -0
- package/dist/markdown/__tests__/parser.test.js.map +1 -0
- package/dist/markdown/__tests__/timesheet-gen.test.d.ts +2 -0
- package/dist/markdown/__tests__/timesheet-gen.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/timesheet-gen.test.js +90 -0
- package/dist/markdown/__tests__/timesheet-gen.test.js.map +1 -0
- package/dist/markdown/auto-slide.d.ts +16 -0
- package/dist/markdown/auto-slide.d.ts.map +1 -0
- package/dist/markdown/auto-slide.js +144 -0
- package/dist/markdown/auto-slide.js.map +1 -0
- package/dist/markdown/compiler.d.ts +12 -0
- package/dist/markdown/compiler.d.ts.map +1 -0
- package/dist/markdown/compiler.js +47 -0
- package/dist/markdown/compiler.js.map +1 -0
- package/dist/markdown/custom-parsers.d.ts +69 -0
- package/dist/markdown/custom-parsers.d.ts.map +1 -0
- package/dist/markdown/custom-parsers.js +227 -0
- package/dist/markdown/custom-parsers.js.map +1 -0
- package/dist/markdown/frontmatter.d.ts +33 -0
- package/dist/markdown/frontmatter.d.ts.map +1 -0
- package/dist/markdown/frontmatter.js +83 -0
- package/dist/markdown/frontmatter.js.map +1 -0
- package/dist/markdown/id.d.ts +4 -0
- package/dist/markdown/id.d.ts.map +1 -0
- package/dist/markdown/id.js +15 -0
- package/dist/markdown/id.js.map +1 -0
- package/dist/markdown/index.d.ts +8 -0
- package/dist/markdown/index.d.ts.map +1 -0
- package/dist/markdown/index.js +6 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markdown/parser.d.ts +35 -0
- package/dist/markdown/parser.d.ts.map +1 -0
- package/dist/markdown/parser.js +605 -0
- package/dist/markdown/parser.js.map +1 -0
- package/dist/markdown/timesheet-gen.d.ts +6 -0
- package/dist/markdown/timesheet-gen.d.ts.map +1 -0
- package/dist/markdown/timesheet-gen.js +223 -0
- package/dist/markdown/timesheet-gen.js.map +1 -0
- package/dist/player/CuePlayer.d.ts +58 -0
- package/dist/player/CuePlayer.d.ts.map +1 -0
- package/dist/player/NavBar.d.ts +31 -0
- package/dist/player/NavBar.d.ts.map +1 -0
- package/dist/player/PresenterView.d.ts +25 -0
- package/dist/player/PresenterView.d.ts.map +1 -0
- package/dist/player/SlideOverview.d.ts +16 -0
- package/dist/player/SlideOverview.d.ts.map +1 -0
- package/dist/player/__mocks__/chart.d.ts +29 -0
- package/dist/player/__mocks__/chart.d.ts.map +1 -0
- package/dist/player/__mocks__/mermaid.d.ts +8 -0
- package/dist/player/__mocks__/mermaid.d.ts.map +1 -0
- package/dist/player/__tests__/animator.test.d.ts +2 -0
- package/dist/player/__tests__/animator.test.d.ts.map +1 -0
- package/dist/player/__tests__/layout.test.d.ts +2 -0
- package/dist/player/__tests__/layout.test.d.ts.map +1 -0
- package/dist/player/__tests__/overview.test.d.ts +2 -0
- package/dist/player/__tests__/overview.test.d.ts.map +1 -0
- package/dist/player/__tests__/timeline.test.d.ts +2 -0
- package/dist/player/__tests__/timeline.test.d.ts.map +1 -0
- package/dist/player/animator.d.ts +4 -0
- package/dist/player/animator.d.ts.map +1 -0
- package/dist/player/cuppacue-player.css +1 -0
- package/dist/player/cuppacue-player.js +3247 -0
- package/dist/player/index.d.ts +14 -0
- package/dist/player/index.d.ts.map +1 -0
- package/dist/player/loader.d.ts +3 -0
- package/dist/player/loader.d.ts.map +1 -0
- package/dist/player/main.d.ts +2 -0
- package/dist/player/main.d.ts.map +1 -0
- package/dist/player/navigator.d.ts +27 -0
- package/dist/player/navigator.d.ts.map +1 -0
- package/dist/player/renderer.d.ts +9 -0
- package/dist/player/renderer.d.ts.map +1 -0
- package/dist/player/renderers/cards.d.ts +3 -0
- package/dist/player/renderers/cards.d.ts.map +1 -0
- package/dist/player/renderers/chart.d.ts +3 -0
- package/dist/player/renderers/chart.d.ts.map +1 -0
- package/dist/player/renderers/icons.d.ts +6 -0
- package/dist/player/renderers/icons.d.ts.map +1 -0
- package/dist/player/renderers/mermaid.d.ts +3 -0
- package/dist/player/renderers/mermaid.d.ts.map +1 -0
- package/dist/player/renderers/stats.d.ts +3 -0
- package/dist/player/renderers/stats.d.ts.map +1 -0
- package/dist/player/renderers/terminal.d.ts +3 -0
- package/dist/player/renderers/terminal.d.ts.map +1 -0
- package/dist/player/themes/dark.d.ts +2 -0
- package/dist/player/themes/dark.d.ts.map +1 -0
- package/dist/player/themes/light.d.ts +2 -0
- package/dist/player/themes/light.d.ts.map +1 -0
- package/dist/player/timeline.d.ts +43 -0
- package/dist/player/timeline.d.ts.map +1 -0
- package/dist/serve.d.ts +2 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +171 -0
- package/dist/serve.js.map +1 -0
- package/dist/templates/discover.d.ts +20 -0
- package/dist/templates/discover.d.ts.map +1 -0
- package/dist/templates/discover.js +86 -0
- package/dist/templates/discover.js.map +1 -0
- package/dist/templates/loader.d.ts +17 -0
- package/dist/templates/loader.d.ts.map +1 -0
- package/dist/templates/loader.js +40 -0
- package/dist/templates/loader.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { parseMarkdown } from "../parser.js";
|
|
3
|
+
import { resetCounters } from "../id.js";
|
|
4
|
+
describe("parseMarkdown", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
resetCounters();
|
|
7
|
+
});
|
|
8
|
+
it("splits scenes on ---", () => {
|
|
9
|
+
const md = `# Scene One
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Scene Two
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Scene Three`;
|
|
18
|
+
const { content } = parseMarkdown(md);
|
|
19
|
+
expect(content.scenes).toHaveLength(3);
|
|
20
|
+
});
|
|
21
|
+
it("maps H1 to title element", () => {
|
|
22
|
+
const { content } = parseMarkdown("# Hello World");
|
|
23
|
+
const el = content.scenes[0].elements[0];
|
|
24
|
+
expect(el.type).toBe("text");
|
|
25
|
+
expect(el).toHaveProperty("role", "title");
|
|
26
|
+
expect(el).toHaveProperty("content", "Hello World");
|
|
27
|
+
});
|
|
28
|
+
it("maps H2 to heading element", () => {
|
|
29
|
+
const { content } = parseMarkdown("## Section Title");
|
|
30
|
+
const el = content.scenes[0].elements[0];
|
|
31
|
+
expect(el.type).toBe("text");
|
|
32
|
+
expect(el).toHaveProperty("role", "heading");
|
|
33
|
+
expect(el).toHaveProperty("content", "Section Title");
|
|
34
|
+
});
|
|
35
|
+
it("maps H3 to subtitle element", () => {
|
|
36
|
+
const { content } = parseMarkdown("### A Subtitle");
|
|
37
|
+
const el = content.scenes[0].elements[0];
|
|
38
|
+
expect(el.type).toBe("text");
|
|
39
|
+
expect(el).toHaveProperty("role", "subtitle");
|
|
40
|
+
});
|
|
41
|
+
it("maps code fences to CodeElement", () => {
|
|
42
|
+
const { content } = parseMarkdown("```typescript\nconst x = 1;\n```");
|
|
43
|
+
const el = content.scenes[0].elements[0];
|
|
44
|
+
expect(el.type).toBe("code");
|
|
45
|
+
expect(el).toHaveProperty("language", "typescript");
|
|
46
|
+
expect(el).toHaveProperty("content", "const x = 1;");
|
|
47
|
+
});
|
|
48
|
+
it("maps blockquote to caption", () => {
|
|
49
|
+
const { content } = parseMarkdown("> A caption");
|
|
50
|
+
const el = content.scenes[0].elements[0];
|
|
51
|
+
expect(el.type).toBe("text");
|
|
52
|
+
expect(el).toHaveProperty("role", "caption");
|
|
53
|
+
});
|
|
54
|
+
it("maps unordered list to ListElement", () => {
|
|
55
|
+
const { content } = parseMarkdown("- One\n- Two\n- Three");
|
|
56
|
+
const el = content.scenes[0].elements[0];
|
|
57
|
+
expect(el.type).toBe("list");
|
|
58
|
+
expect(el).toHaveProperty("items", ["One", "Two", "Three"]);
|
|
59
|
+
expect(el).toHaveProperty("ordered", false);
|
|
60
|
+
});
|
|
61
|
+
it("maps ordered list", () => {
|
|
62
|
+
const { content } = parseMarkdown("1. First\n2. Second");
|
|
63
|
+
const el = content.scenes[0].elements[0];
|
|
64
|
+
expect(el.type).toBe("list");
|
|
65
|
+
expect(el).toHaveProperty("ordered", true);
|
|
66
|
+
});
|
|
67
|
+
it("generates unique IDs across elements", () => {
|
|
68
|
+
const { content } = parseMarkdown("# Title\n\n### Subtitle\n\nParagraph");
|
|
69
|
+
const ids = content.scenes[0].elements.map((el) => el.id);
|
|
70
|
+
const unique = new Set(ids);
|
|
71
|
+
expect(unique.size).toBe(ids.length);
|
|
72
|
+
});
|
|
73
|
+
it("maps image in paragraph to ImageElement", () => {
|
|
74
|
+
const { content } = parseMarkdown("");
|
|
75
|
+
const el = content.scenes[0].elements[0];
|
|
76
|
+
expect(el.type).toBe("image");
|
|
77
|
+
expect(el).toHaveProperty("src", "image.png");
|
|
78
|
+
expect(el).toHaveProperty("alt", "Alt text");
|
|
79
|
+
});
|
|
80
|
+
it("assigns layout to each scene", () => {
|
|
81
|
+
const { content } = parseMarkdown("# Title\n\n---\n\n## Heading\n\n- Item 1\n- Item 2");
|
|
82
|
+
expect(content.scenes[0].layout).toBe("intro");
|
|
83
|
+
expect(content.scenes[1].layout).toBe("header-body");
|
|
84
|
+
});
|
|
85
|
+
it("does not set position fields on elements", () => {
|
|
86
|
+
const { content } = parseMarkdown("# Title\n\n```ts\nconst x = 1;\n```");
|
|
87
|
+
for (const el of content.scenes[0].elements) {
|
|
88
|
+
expect(el).not.toHaveProperty("position");
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// ── Speaker Notes ───────────────────────────────────────
|
|
92
|
+
it("extracts notes from HTML comment", () => {
|
|
93
|
+
const { content } = parseMarkdown("# Title\n\n<!-- notes: Remember to explain this -->");
|
|
94
|
+
expect(content.scenes[0].notes).toBe("Remember to explain this");
|
|
95
|
+
});
|
|
96
|
+
it("extracts multi-line notes", () => {
|
|
97
|
+
const { content } = parseMarkdown("# Title\n\n<!-- notes: Line one\nLine two\nLine three -->");
|
|
98
|
+
expect(content.scenes[0].notes).toBe("Line one\nLine two\nLine three");
|
|
99
|
+
});
|
|
100
|
+
it("does not render notes comment as element", () => {
|
|
101
|
+
const { content } = parseMarkdown("# Title\n\n<!-- notes: secret -->");
|
|
102
|
+
expect(content.scenes[0].elements).toHaveLength(1);
|
|
103
|
+
expect(content.scenes[0].elements[0].type).toBe("text");
|
|
104
|
+
});
|
|
105
|
+
it("scene without notes has no notes field", () => {
|
|
106
|
+
const { content } = parseMarkdown("# Title");
|
|
107
|
+
expect(content.scenes[0]).not.toHaveProperty("notes");
|
|
108
|
+
});
|
|
109
|
+
// ── Inline Formatting ────────────────────────────────────
|
|
110
|
+
it("preserves bold formatting in paragraph", () => {
|
|
111
|
+
const { content } = parseMarkdown("This is **bold** text");
|
|
112
|
+
const el = content.scenes[0].elements[0];
|
|
113
|
+
expect(el).toHaveProperty("content");
|
|
114
|
+
expect(el.content).toContain("<strong>bold</strong>");
|
|
115
|
+
});
|
|
116
|
+
it("preserves italic formatting", () => {
|
|
117
|
+
const { content } = parseMarkdown("This is *italic* text");
|
|
118
|
+
const el = content.scenes[0].elements[0];
|
|
119
|
+
expect(el.content).toContain("<em>italic</em>");
|
|
120
|
+
});
|
|
121
|
+
it("preserves inline code", () => {
|
|
122
|
+
const { content } = parseMarkdown("Use `const x = 1` here");
|
|
123
|
+
const el = content.scenes[0].elements[0];
|
|
124
|
+
expect(el.content).toContain("<code>const x = 1</code>");
|
|
125
|
+
});
|
|
126
|
+
it("preserves strikethrough", () => {
|
|
127
|
+
const { content } = parseMarkdown("This is ~~wrong~~ correct");
|
|
128
|
+
const el = content.scenes[0].elements[0];
|
|
129
|
+
expect(el.content).toContain("<del>wrong</del>");
|
|
130
|
+
});
|
|
131
|
+
it("preserves markdown links", () => {
|
|
132
|
+
const { content } = parseMarkdown("Visit [Google](https://google.com)");
|
|
133
|
+
const el = content.scenes[0].elements[0];
|
|
134
|
+
expect(el.content).toContain('<a href="https://google.com"');
|
|
135
|
+
expect(el.content).toContain("Google</a>");
|
|
136
|
+
});
|
|
137
|
+
it("preserves inline formatting in headings", () => {
|
|
138
|
+
const { content } = parseMarkdown("# Hello **World**");
|
|
139
|
+
const el = content.scenes[0].elements[0];
|
|
140
|
+
expect(el.content).toContain("<strong>World</strong>");
|
|
141
|
+
});
|
|
142
|
+
it("preserves inline formatting in list items", () => {
|
|
143
|
+
const { content } = parseMarkdown("- **Bold** item\n- *Italic* item");
|
|
144
|
+
const el = content.scenes[0].elements[0];
|
|
145
|
+
expect(el.type).toBe("list");
|
|
146
|
+
expect(el.items[0]).toContain("<strong>Bold</strong>");
|
|
147
|
+
expect(el.items[1]).toContain("<em>Italic</em>");
|
|
148
|
+
});
|
|
149
|
+
it("escapes HTML in text", () => {
|
|
150
|
+
const { content } = parseMarkdown("Use <script>alert('xss')</script> carefully");
|
|
151
|
+
const el = content.scenes[0].elements[0];
|
|
152
|
+
expect(el.content).not.toContain("<script>");
|
|
153
|
+
expect(el.content).toContain("<script>");
|
|
154
|
+
});
|
|
155
|
+
it("plain text without formatting is unchanged", () => {
|
|
156
|
+
const { content } = parseMarkdown("Plain text paragraph");
|
|
157
|
+
const el = content.scenes[0].elements[0];
|
|
158
|
+
expect(el.content).toBe("Plain text paragraph");
|
|
159
|
+
});
|
|
160
|
+
// ── Tables ───────────────────────────────────────────────
|
|
161
|
+
it("maps markdown table to TableElement", () => {
|
|
162
|
+
const md = "| Name | Age |\n| --- | --- |\n| Alice | 30 |\n| Bob | 25 |";
|
|
163
|
+
const { content } = parseMarkdown(md);
|
|
164
|
+
const el = content.scenes[0].elements[0];
|
|
165
|
+
expect(el.type).toBe("table");
|
|
166
|
+
expect(el).toHaveProperty("headers", ["Name", "Age"]);
|
|
167
|
+
expect(el).toHaveProperty("rows", [["Alice", "30"], ["Bob", "25"]]);
|
|
168
|
+
});
|
|
169
|
+
it("preserves table column alignments", () => {
|
|
170
|
+
const md = "| Left | Center | Right |\n| :--- | :---: | ---: |\n| a | b | c |";
|
|
171
|
+
const { content } = parseMarkdown(md);
|
|
172
|
+
const el = content.scenes[0].elements[0];
|
|
173
|
+
expect(el.alignments).toEqual(["left", "center", "right"]);
|
|
174
|
+
});
|
|
175
|
+
it("supports inline formatting in table cells", () => {
|
|
176
|
+
const md = "| Feature | Status |\n| --- | --- |\n| **Bold** | *Done* |";
|
|
177
|
+
const { content } = parseMarkdown(md);
|
|
178
|
+
const el = content.scenes[0].elements[0];
|
|
179
|
+
expect(el.rows[0][0]).toContain("<strong>Bold</strong>");
|
|
180
|
+
expect(el.rows[0][1]).toContain("<em>Done</em>");
|
|
181
|
+
});
|
|
182
|
+
it("infers header-body layout for heading + table", () => {
|
|
183
|
+
const md = "## Comparison\n\n| A | B |\n| --- | --- |\n| 1 | 2 |";
|
|
184
|
+
const { content } = parseMarkdown(md);
|
|
185
|
+
expect(content.scenes[0].layout).toBe("header-body");
|
|
186
|
+
});
|
|
187
|
+
// ── Custom Element Types ────────────────────────────────────
|
|
188
|
+
it("maps ```mermaid to MermaidElement", () => {
|
|
189
|
+
const md = "```mermaid\nflowchart LR\n A --> B\n```";
|
|
190
|
+
const { content } = parseMarkdown(md);
|
|
191
|
+
const el = content.scenes[0].elements[0];
|
|
192
|
+
expect(el.type).toBe("mermaid");
|
|
193
|
+
expect(el).toHaveProperty("definition", "flowchart LR\n A --> B");
|
|
194
|
+
});
|
|
195
|
+
it("maps ```chart:bar to ChartElement", () => {
|
|
196
|
+
const md = "```chart:bar\n| X | Y |\n| A | 10 |\n```";
|
|
197
|
+
const { content } = parseMarkdown(md);
|
|
198
|
+
const el = content.scenes[0].elements[0];
|
|
199
|
+
expect(el.type).toBe("chart");
|
|
200
|
+
expect(el).toHaveProperty("chartType", "bar");
|
|
201
|
+
});
|
|
202
|
+
it("maps ```chart:pie to ChartElement", () => {
|
|
203
|
+
const md = "```chart:pie\n| Cat | Count |\n| Dogs | 60 |\n| Cats | 40 |\n```";
|
|
204
|
+
const { content } = parseMarkdown(md);
|
|
205
|
+
const el = content.scenes[0].elements[0];
|
|
206
|
+
expect(el.type).toBe("chart");
|
|
207
|
+
expect(el).toHaveProperty("chartType", "pie");
|
|
208
|
+
});
|
|
209
|
+
it("maps ```cards to CardsElement", () => {
|
|
210
|
+
const md = "```cards\n- title: Fast\n icon: zap\n desc: Quick builds\n```";
|
|
211
|
+
const { content } = parseMarkdown(md);
|
|
212
|
+
const el = content.scenes[0].elements[0];
|
|
213
|
+
expect(el.type).toBe("cards");
|
|
214
|
+
expect(el).toHaveProperty("cards");
|
|
215
|
+
expect(el.cards).toHaveLength(1);
|
|
216
|
+
});
|
|
217
|
+
it("maps ```stats to StatsElement", () => {
|
|
218
|
+
const md = "```stats\n- 100 | Users\n- 50 | Projects\n```";
|
|
219
|
+
const { content } = parseMarkdown(md);
|
|
220
|
+
const el = content.scenes[0].elements[0];
|
|
221
|
+
expect(el.type).toBe("stats");
|
|
222
|
+
expect(el).toHaveProperty("stats");
|
|
223
|
+
expect(el.stats).toHaveLength(2);
|
|
224
|
+
});
|
|
225
|
+
it("maps ```terminal to TerminalElement", () => {
|
|
226
|
+
const md = "```terminal\n$ echo hello\nhello\n```";
|
|
227
|
+
const { content } = parseMarkdown(md);
|
|
228
|
+
const el = content.scenes[0].elements[0];
|
|
229
|
+
expect(el.type).toBe("terminal");
|
|
230
|
+
expect(el).toHaveProperty("lines");
|
|
231
|
+
expect(el.lines).toHaveLength(2);
|
|
232
|
+
});
|
|
233
|
+
it("infers header-body layout for heading + custom element", () => {
|
|
234
|
+
const md = "## Metrics\n\n```stats\n- 100 | Users\n```";
|
|
235
|
+
const { content } = parseMarkdown(md);
|
|
236
|
+
expect(content.scenes[0].layout).toBe("header-body");
|
|
237
|
+
});
|
|
238
|
+
it("infers full layout for standalone custom element", () => {
|
|
239
|
+
const md = "```chart:bar\n| X | Y |\n| A | 10 |\n```";
|
|
240
|
+
const { content } = parseMarkdown(md);
|
|
241
|
+
expect(content.scenes[0].layout).toBe("full");
|
|
242
|
+
});
|
|
243
|
+
it("regular code blocks are not affected by custom parsing", () => {
|
|
244
|
+
const md = "```typescript\nconst x = 1;\n```";
|
|
245
|
+
const { content } = parseMarkdown(md);
|
|
246
|
+
const el = content.scenes[0].elements[0];
|
|
247
|
+
expect(el.type).toBe("code");
|
|
248
|
+
expect(el).toHaveProperty("language", "typescript");
|
|
249
|
+
});
|
|
250
|
+
it("tracks local image resources", () => {
|
|
251
|
+
const md = "";
|
|
252
|
+
const { localResources } = parseMarkdown(md);
|
|
253
|
+
expect(localResources).toContain("./images/arch.png");
|
|
254
|
+
});
|
|
255
|
+
it("does not track external image URLs as resources", () => {
|
|
256
|
+
const md = "";
|
|
257
|
+
const { localResources } = parseMarkdown(md);
|
|
258
|
+
expect(localResources).toHaveLength(0);
|
|
259
|
+
});
|
|
260
|
+
// ── Per-Slide Frontmatter ─────────────────────────────────
|
|
261
|
+
it("applies layout override from per-slide frontmatter", () => {
|
|
262
|
+
const md = `# Slide 1
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
layout: split
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Slide 2
|
|
269
|
+
|
|
270
|
+
Some body text`;
|
|
271
|
+
const { content } = parseMarkdown(md);
|
|
272
|
+
expect(content.scenes).toHaveLength(2);
|
|
273
|
+
expect(content.scenes[1].layout).toBe("split");
|
|
274
|
+
});
|
|
275
|
+
it("applies background from per-slide frontmatter", () => {
|
|
276
|
+
const md = `# Slide 1
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
background: "#1a1a2e"
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Slide 2`;
|
|
283
|
+
const { content } = parseMarkdown(md);
|
|
284
|
+
expect(content.scenes[1].background).toBe("#1a1a2e");
|
|
285
|
+
});
|
|
286
|
+
it("applies transition from per-slide frontmatter", () => {
|
|
287
|
+
const md = `# Slide 1
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
transition: fade
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Slide 2`;
|
|
294
|
+
const { content } = parseMarkdown(md);
|
|
295
|
+
expect(content.scenes[1].transition).toEqual({ type: "fade" });
|
|
296
|
+
});
|
|
297
|
+
it("applies class from per-slide frontmatter", () => {
|
|
298
|
+
const md = `# Slide 1
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
class: emphasis
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Slide 2`;
|
|
305
|
+
const { content } = parseMarkdown(md);
|
|
306
|
+
expect(content.scenes[1].class).toBe("emphasis");
|
|
307
|
+
});
|
|
308
|
+
it("injects image element from frontmatter image field", () => {
|
|
309
|
+
const md = `# Slide 1
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
layout: image-right
|
|
313
|
+
image: ./images/photo.jpg
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Slide 2`;
|
|
317
|
+
const { content } = parseMarkdown(md);
|
|
318
|
+
expect(content.scenes[1].layout).toBe("image-right");
|
|
319
|
+
const imgEl = content.scenes[1].elements.find(e => e.type === "image");
|
|
320
|
+
expect(imgEl).toBeDefined();
|
|
321
|
+
expect(imgEl).toHaveProperty("src", "./images/photo.jpg");
|
|
322
|
+
});
|
|
323
|
+
it("tracks frontmatter image as local resource", () => {
|
|
324
|
+
const md = `---
|
|
325
|
+
layout: intro
|
|
326
|
+
image: ./images/logo.png
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
# Title`;
|
|
330
|
+
const { localResources } = parseMarkdown(md);
|
|
331
|
+
expect(localResources).toContain("./images/logo.png");
|
|
332
|
+
});
|
|
333
|
+
it("slides without frontmatter still work normally", () => {
|
|
334
|
+
const md = `# Slide 1
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Slide 2
|
|
339
|
+
|
|
340
|
+
- Item A
|
|
341
|
+
- Item B`;
|
|
342
|
+
const { content } = parseMarkdown(md);
|
|
343
|
+
expect(content.scenes).toHaveLength(2);
|
|
344
|
+
expect(content.scenes[0].layout).toBe("intro");
|
|
345
|
+
expect(content.scenes[1].layout).toBe("header-body");
|
|
346
|
+
});
|
|
347
|
+
it("handles multiple slides with mixed frontmatter", () => {
|
|
348
|
+
const md = `# Title
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
layout: section
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Part One
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Part Two
|
|
359
|
+
|
|
360
|
+
- Detail`;
|
|
361
|
+
const { content } = parseMarkdown(md);
|
|
362
|
+
expect(content.scenes).toHaveLength(3);
|
|
363
|
+
expect(content.scenes[0].layout).toBe("intro");
|
|
364
|
+
expect(content.scenes[1].layout).toBe("section");
|
|
365
|
+
expect(content.scenes[2].layout).toBe("header-body");
|
|
366
|
+
});
|
|
367
|
+
it("does not treat regular text with colons as frontmatter", () => {
|
|
368
|
+
const md = `# Slide 1
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
Author: John Doe wrote this
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Slide 3`;
|
|
377
|
+
const { content } = parseMarkdown(md);
|
|
378
|
+
// "Author:" is not a known FM key, so it's treated as slide content
|
|
379
|
+
expect(content.scenes).toHaveLength(3);
|
|
380
|
+
expect(content.scenes[1].elements.length).toBeGreaterThan(0);
|
|
381
|
+
});
|
|
382
|
+
// ── Background Image Resources ──────────────────────────────
|
|
383
|
+
it("tracks background image path as local resource", () => {
|
|
384
|
+
const md = `# Slide 1
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
background: ./images/hero.jpg
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Slide 2`;
|
|
391
|
+
const { localResources } = parseMarkdown(md);
|
|
392
|
+
expect(localResources).toContain("./images/hero.jpg");
|
|
393
|
+
});
|
|
394
|
+
it("does not track background color as resource", () => {
|
|
395
|
+
const md = `# Slide 1
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
background: "#1a1a2e"
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Slide 2`;
|
|
402
|
+
const { localResources } = parseMarkdown(md);
|
|
403
|
+
expect(localResources).toHaveLength(0);
|
|
404
|
+
});
|
|
405
|
+
it("does not track external background URL as resource", () => {
|
|
406
|
+
const md = `# Slide 1
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
background: https://example.com/hero.jpg
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Slide 2`;
|
|
413
|
+
const { localResources } = parseMarkdown(md);
|
|
414
|
+
expect(localResources).toHaveLength(0);
|
|
415
|
+
});
|
|
416
|
+
// ── Footer Directive ────────────────────────────────────
|
|
417
|
+
it("extracts footer from HTML comment directive", () => {
|
|
418
|
+
const { content } = parseMarkdown("# Title\n\n<!-- footer: Q4 2025 Report -->");
|
|
419
|
+
const footer = content.scenes[0].elements.find((e) => e.type === "text" && e.role === "footer");
|
|
420
|
+
expect(footer).toBeDefined();
|
|
421
|
+
expect(footer).toHaveProperty("content", "Q4 2025 Report");
|
|
422
|
+
});
|
|
423
|
+
it("footer element does not affect layout inference", () => {
|
|
424
|
+
const { content } = parseMarkdown("# Title\n\n<!-- footer: Some text -->");
|
|
425
|
+
// Title-only slide should still be intro, not header-body
|
|
426
|
+
expect(content.scenes[0].layout).toBe("intro");
|
|
427
|
+
});
|
|
428
|
+
// ── AccentBar Frontmatter ──────────────────────────────
|
|
429
|
+
it("applies accentBar from per-slide frontmatter", () => {
|
|
430
|
+
const md = `# Slide 1
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
accentBar: "#38bdf8"
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Slide 2`;
|
|
437
|
+
const { content } = parseMarkdown(md);
|
|
438
|
+
expect(content.scenes[1].accentBar).toBeDefined();
|
|
439
|
+
expect(content.scenes[1].accentBar.color).toBe("#38bdf8");
|
|
440
|
+
});
|
|
441
|
+
it("parses accentBar with position", () => {
|
|
442
|
+
const md = `# Slide 1
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
accentBar: "#38bdf8 bottom"
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Slide 2`;
|
|
449
|
+
const { content } = parseMarkdown(md);
|
|
450
|
+
expect(content.scenes[1].accentBar).toBeDefined();
|
|
451
|
+
expect(content.scenes[1].accentBar.color).toBe("#38bdf8");
|
|
452
|
+
expect(content.scenes[1].accentBar.position).toBe("bottom");
|
|
453
|
+
});
|
|
454
|
+
// ── Scene Templates ───────────────────────────────────────
|
|
455
|
+
it("auto-assigns intro template to first scene", () => {
|
|
456
|
+
const md = `# Title
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Content
|
|
461
|
+
|
|
462
|
+
- Item 1
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Thank You`;
|
|
467
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
468
|
+
expect(content.scenes[0].sceneTemplate).toBe("tech-talk/intro");
|
|
469
|
+
});
|
|
470
|
+
it("auto-assigns closing template to last scene", () => {
|
|
471
|
+
const md = `# Title
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Content
|
|
476
|
+
|
|
477
|
+
- Item 1
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Thank You`;
|
|
482
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
483
|
+
expect(content.scenes[2].sceneTemplate).toBe("tech-talk/closing");
|
|
484
|
+
});
|
|
485
|
+
it("auto-assigns section template to heading-only scenes", () => {
|
|
486
|
+
const md = `# Title
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Section Divider
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Content
|
|
495
|
+
|
|
496
|
+
- Item 1
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Thank You`;
|
|
501
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
502
|
+
expect(content.scenes[1].sceneTemplate).toBe("tech-talk/section");
|
|
503
|
+
});
|
|
504
|
+
it("auto-assigns content template to list scenes", () => {
|
|
505
|
+
const md = `# Title
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Content
|
|
510
|
+
|
|
511
|
+
- Item 1
|
|
512
|
+
- Item 2
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Thank You`;
|
|
517
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
518
|
+
expect(content.scenes[1].sceneTemplate).toBe("tech-talk/content");
|
|
519
|
+
});
|
|
520
|
+
it("auto-assigns code template to scenes with code elements", () => {
|
|
521
|
+
const md = `# Title
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## Example
|
|
526
|
+
|
|
527
|
+
\`\`\`typescript
|
|
528
|
+
const x = 1;
|
|
529
|
+
\`\`\`
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Thank You`;
|
|
534
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
535
|
+
expect(content.scenes[1].sceneTemplate).toBe("tech-talk/code");
|
|
536
|
+
});
|
|
537
|
+
it("code template takes priority over content when both code and list present", () => {
|
|
538
|
+
const md = `# Title
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Mixed
|
|
543
|
+
|
|
544
|
+
- Point one
|
|
545
|
+
|
|
546
|
+
\`\`\`typescript
|
|
547
|
+
const x = 1;
|
|
548
|
+
\`\`\`
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Thank You`;
|
|
553
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
554
|
+
expect(content.scenes[1].sceneTemplate).toBe("tech-talk/code");
|
|
555
|
+
});
|
|
556
|
+
it("per-slide template override is not overwritten by auto-assignment", () => {
|
|
557
|
+
const md = `---
|
|
558
|
+
template: custom/intro
|
|
559
|
+
badge: "MY TALK"
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
# Title
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Thank You`;
|
|
567
|
+
const { content } = parseMarkdown(md, { presentationTemplate: "tech-talk" });
|
|
568
|
+
expect(content.scenes[0].sceneTemplate).toBe("custom/intro");
|
|
569
|
+
expect(content.scenes[0].templateData).toEqual({ badge: "MY TALK" });
|
|
570
|
+
});
|
|
571
|
+
it("collects templateData from frontmatter", () => {
|
|
572
|
+
const md = `---
|
|
573
|
+
template: tech-talk/intro
|
|
574
|
+
badge: "TECH TALK"
|
|
575
|
+
footerKeywords: "Fast · Reliable · Open Source"
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
# Title`;
|
|
579
|
+
const { content } = parseMarkdown(md);
|
|
580
|
+
expect(content.scenes[0].sceneTemplate).toBe("tech-talk/intro");
|
|
581
|
+
expect(content.scenes[0].templateData).toEqual({
|
|
582
|
+
badge: "TECH TALK",
|
|
583
|
+
footerKeywords: "Fast · Reliable · Open Source",
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
it("non-templated presentations are unchanged", () => {
|
|
587
|
+
const md = `# Title
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Content
|
|
592
|
+
|
|
593
|
+
- Item`;
|
|
594
|
+
const { content } = parseMarkdown(md);
|
|
595
|
+
expect(content.scenes[0].sceneTemplate).toBeUndefined();
|
|
596
|
+
expect(content.scenes[1].sceneTemplate).toBeUndefined();
|
|
597
|
+
});
|
|
598
|
+
// ── Code Highlight Lines ─────────────────────────────────
|
|
599
|
+
it("parses highlight lines from code fence info string", () => {
|
|
600
|
+
const md = "```typescript {1-3,5}\nconst a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nconst e = 5;\n```";
|
|
601
|
+
const { content } = parseMarkdown(md);
|
|
602
|
+
const el = content.scenes[0].elements[0];
|
|
603
|
+
expect(el.type).toBe("code");
|
|
604
|
+
expect(el).toHaveProperty("highlightLines", [1, 2, 3, 5]);
|
|
605
|
+
expect(el).toHaveProperty("language", "typescript");
|
|
606
|
+
});
|
|
607
|
+
it("parses single line highlight", () => {
|
|
608
|
+
const md = "```python {3}\nprint('a')\nprint('b')\nprint('c')\n```";
|
|
609
|
+
const { content } = parseMarkdown(md);
|
|
610
|
+
const el = content.scenes[0].elements[0];
|
|
611
|
+
expect(el).toHaveProperty("highlightLines", [3]);
|
|
612
|
+
expect(el).toHaveProperty("language", "python");
|
|
613
|
+
});
|
|
614
|
+
it("code without highlight lines has no highlightLines field", () => {
|
|
615
|
+
const md = "```typescript\nconst x = 1;\n```";
|
|
616
|
+
const { content } = parseMarkdown(md);
|
|
617
|
+
const el = content.scenes[0].elements[0];
|
|
618
|
+
expect(el).not.toHaveProperty("highlightLines");
|
|
619
|
+
});
|
|
620
|
+
it("parses multiple ranges in highlight lines", () => {
|
|
621
|
+
const md = "```js {1-2,4-6,8}\nline1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\n```";
|
|
622
|
+
const { content } = parseMarkdown(md);
|
|
623
|
+
const el = content.scenes[0].elements[0];
|
|
624
|
+
expect(el).toHaveProperty("highlightLines", [1, 2, 4, 5, 6, 8]);
|
|
625
|
+
});
|
|
626
|
+
it("does not split on --- inside code fences", () => {
|
|
627
|
+
const md = `# Slide 1
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## Slide 2
|
|
632
|
+
|
|
633
|
+
\`\`\`markdown
|
|
634
|
+
---
|
|
635
|
+
layout: split
|
|
636
|
+
---
|
|
637
|
+
\`\`\``;
|
|
638
|
+
const { content } = parseMarkdown(md);
|
|
639
|
+
expect(content.scenes).toHaveLength(2);
|
|
640
|
+
// The code block should contain the --- lines
|
|
641
|
+
const codeEl = content.scenes[1].elements.find(e => e.type === "code");
|
|
642
|
+
expect(codeEl).toBeDefined();
|
|
643
|
+
expect(codeEl.content).toContain("---");
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
//# sourceMappingURL=parser.test.js.map
|