supadeck 0.0.5 → 0.0.7
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/dist/cli/index.js +30532 -88
- package/dist/content/rehype-shiki-code-blocks.d.ts +9 -0
- package/dist/index.js +2808 -11
- package/dist/metafile-esm.json +1 -0
- package/dist/runtime/main.js +2868 -21
- package/dist/runtime/themes/base/theme.css +183 -145
- package/dist/runtime/themes/default/index.js +312 -15
- package/dist/runtime/themes/default/theme.css +17 -0
- package/dist/runtime/themes/sunset/index.js +2737 -10
- package/dist/runtime/vite-config.js +30206 -196
- package/package.json +16 -10
- package/dist/cli/export.js +0 -35
- package/dist/cli/serve.js +0 -12
- package/dist/cli/templates.js +0 -69
- package/dist/cli/workspace.js +0 -26
- package/dist/content/parse-deck.js +0 -136
- package/dist/content/remark-unwrap-jsx-paragraphs.js +0 -63
- package/dist/export/pdf.js +0 -40
- package/dist/runtime/App.js +0 -45
- package/dist/runtime/components/Callout.js +0 -13
- package/dist/runtime/components/Center.js +0 -4
- package/dist/runtime/components/Columns.js +0 -4
- package/dist/runtime/components/Disclosure.js +0 -5
- package/dist/runtime/components/Frame.js +0 -4
- package/dist/runtime/components/index.js +0 -4
- package/dist/runtime/default-components.js +0 -38
- package/dist/runtime/hooks/slides.js +0 -45
- package/dist/runtime/layout/DeckSlide.js +0 -6
- package/dist/runtime/layout/SlideFrame.js +0 -6
- package/dist/runtime/mdx-components.d.ts +0 -2
- package/dist/runtime/mdx-components.js +0 -17
- package/dist/runtime/primitives/DeckChrome.js +0 -6
- package/dist/runtime/primitives/DeckNavigation.js +0 -4
- package/dist/runtime/primitives/DeckProgress.js +0 -5
- package/dist/runtime/primitives/DeckTitle.js +0 -4
- package/dist/runtime/public-components.d.ts +0 -22
- package/dist/runtime/public-components.js +0 -24
- package/dist/runtime/styles/base.css +0 -201
- package/dist/runtime/tailwind-hmr.js +0 -67
- package/dist/runtime/tailwind-sources.js +0 -42
- package/dist/runtime/theme-components.d.ts +0 -3
- package/dist/runtime/theme-components.js +0 -18
- package/dist/runtime/theme-resolution.js +0 -62
- package/dist/runtime/theme-types.js +0 -1
- package/dist/runtime/themes/base/DefaultDeck.js +0 -10
- package/dist/runtime/themes/default/DefaultDeck.d.ts +0 -2
- package/dist/runtime/themes/default/DefaultDeck.js +0 -11
- package/dist/runtime/themes/default/DefaultThemeDeck.js +0 -22
- package/dist/runtime/themes/default/components.js +0 -79
- package/dist/runtime/themes/default.css +0 -10
- package/dist/runtime/themes/sunset.css +0 -10
- package/dist/runtime/themes/supabase/SupabaseDeck.d.ts +0 -2
- package/dist/runtime/themes/supabase/SupabaseDeck.js +0 -23
- package/dist/runtime/themes/supabase/components.d.ts +0 -65
- package/dist/runtime/themes/supabase/components.js +0 -80
- package/dist/runtime/themes/supabase/index.d.ts +0 -4
- package/dist/runtime/themes/supabase/index.js +0 -17
- package/dist/runtime/themes/supabase/theme.css +0 -523
- package/dist/runtime/utils/use-current-slide.js +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supadeck",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Zero-config MDX presentations with hot reload and PDF export.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"README.md"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "tsc -p tsconfig.build.json && node ./scripts/copy-static.mjs",
|
|
18
|
+
"build": "tsup --config tsup.config.ts && tsc -p tsconfig.build.json --emitDeclarationOnly && node ./scripts/copy-static.mjs",
|
|
19
19
|
"dev": "SUPADECK_DEFAULT_INPUT=examples/dev tsx ./src/cli/index.ts",
|
|
20
20
|
"export": "SUPADECK_DEFAULT_INPUT=examples/dev tsx ./src/cli/index.ts export",
|
|
21
21
|
"smoke:npx": "npm run build && node ./scripts/smoke-npx.mjs",
|
|
@@ -35,27 +35,33 @@
|
|
|
35
35
|
],
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@base-ui-components/react": "^1.0.0-rc.0",
|
|
39
38
|
"@mdx-js/rollup": "^3.1.0",
|
|
40
39
|
"@tailwindcss/vite": "^4.1.0",
|
|
41
40
|
"@vitejs/plugin-react": "^5.0.0",
|
|
42
|
-
"gray-matter": "^4.0.3",
|
|
43
41
|
"playwright": "^1.54.0",
|
|
44
|
-
"react": "^19.1.0",
|
|
45
|
-
"react-dom": "^19.1.0",
|
|
46
|
-
"remark-gfm": "^4.0.1",
|
|
47
|
-
"remark-mdx": "^3.1.0",
|
|
48
|
-
"remark-parse": "^11.0.0",
|
|
49
42
|
"tailwindcss": "^4.1.0",
|
|
50
|
-
"unified": "^11.0.5",
|
|
51
43
|
"vite": "^7.0.0"
|
|
52
44
|
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": "^19.1.0",
|
|
47
|
+
"react-dom": "^19.1.0",
|
|
48
|
+
"shiki": "^3.14.0"
|
|
49
|
+
},
|
|
53
50
|
"devDependencies": {
|
|
51
|
+
"@base-ui/react": "^1.3.0",
|
|
54
52
|
"@types/node": "^24.0.0",
|
|
55
53
|
"@types/react": "^19.1.0",
|
|
56
54
|
"@types/react-dom": "^19.1.0",
|
|
55
|
+
"gray-matter": "^4.0.3",
|
|
56
|
+
"react": "^19.1.0",
|
|
57
|
+
"react-dom": "^19.1.0",
|
|
58
|
+
"remark-gfm": "^4.0.1",
|
|
59
|
+
"remark-mdx": "^3.1.0",
|
|
60
|
+
"remark-parse": "^11.0.0",
|
|
61
|
+
"tsup": "^8.5.0",
|
|
57
62
|
"tsx": "^4.19.0",
|
|
58
63
|
"typescript": "^5.8.0",
|
|
64
|
+
"unified": "^11.0.5",
|
|
59
65
|
"vitest": "^3.2.4"
|
|
60
66
|
}
|
|
61
67
|
}
|
package/dist/cli/export.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { build, preview } from "vite";
|
|
2
|
-
import { exportDeckToPdf } from "../export/pdf.js";
|
|
3
|
-
import { createSupadeckViteConfig } from "../runtime/vite-config.js";
|
|
4
|
-
export async function runExport({ deckPath, outputPath, themeOverride, }) {
|
|
5
|
-
const config = createSupadeckViteConfig({
|
|
6
|
-
deckPath,
|
|
7
|
-
themeOverride,
|
|
8
|
-
outputDirName: ".supadeck-dist",
|
|
9
|
-
});
|
|
10
|
-
await build(config);
|
|
11
|
-
const previewServer = await preview({
|
|
12
|
-
...config,
|
|
13
|
-
preview: {
|
|
14
|
-
host: "127.0.0.1",
|
|
15
|
-
port: 4173,
|
|
16
|
-
strictPort: false,
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
const urls = previewServer.resolvedUrls?.local ?? [];
|
|
20
|
-
const baseUrl = urls[0];
|
|
21
|
-
if (!baseUrl) {
|
|
22
|
-
await previewServer.httpServer?.close();
|
|
23
|
-
throw new Error("Unable to determine preview URL for PDF export.");
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
await exportDeckToPdf({
|
|
27
|
-
url: `${baseUrl}?print=1`,
|
|
28
|
-
outputPath,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
finally {
|
|
32
|
-
await previewServer.httpServer?.close();
|
|
33
|
-
}
|
|
34
|
-
console.log(`[supadeck] PDF exported to ${outputPath}`);
|
|
35
|
-
}
|
package/dist/cli/serve.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { createServer } from "vite";
|
|
2
|
-
import { createSupadeckViteConfig } from "../runtime/vite-config.js";
|
|
3
|
-
export async function runDevServer({ deckPath, port, open, themeOverride, }) {
|
|
4
|
-
const server = await createServer(createSupadeckViteConfig({
|
|
5
|
-
deckPath,
|
|
6
|
-
port,
|
|
7
|
-
open,
|
|
8
|
-
themeOverride,
|
|
9
|
-
}));
|
|
10
|
-
await server.listen();
|
|
11
|
-
server.printUrls();
|
|
12
|
-
}
|
package/dist/cli/templates.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export function starterDeckTemplate() {
|
|
2
|
-
return `---
|
|
3
|
-
title: Supadeck
|
|
4
|
-
theme: default
|
|
5
|
-
aspectRatio: 16:9
|
|
6
|
-
showSlideNumbers: true
|
|
7
|
-
transition: fade
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# *Supa*deck
|
|
11
|
-
|
|
12
|
-
Write presentations in one \`deck.mdx\` file.
|
|
13
|
-
|
|
14
|
-
<Callout tone="accent">
|
|
15
|
-
Edit this file and the deck will hot reload automatically.
|
|
16
|
-
</Callout>
|
|
17
|
-
|
|
18
|
-
Use arrow keys to navigate.
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Use *markdown* to write your slides.
|
|
23
|
-
|
|
24
|
-
\`\`\`md
|
|
25
|
-
Lorem ipsum *dolor* sit **amet**...
|
|
26
|
-
|
|
27
|
-
- item 1
|
|
28
|
-
- item 2
|
|
29
|
-
- item 3
|
|
30
|
-
\`\`\`
|
|
31
|
-
|
|
32
|
-
Lorem ipsum *dolor* sit **amet**...
|
|
33
|
-
|
|
34
|
-
- item 1
|
|
35
|
-
- item 2
|
|
36
|
-
- item 3
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Or use *custom components*
|
|
41
|
-
|
|
42
|
-
<Columns
|
|
43
|
-
left={<Frame label="Local imports">Import your own React components only when you need them.</Frame>}
|
|
44
|
-
right={<Disclosure title="Theme-provided components">Themes can expose components directly to MDX.</Disclosure>}
|
|
45
|
-
/>
|
|
46
|
-
|
|
47
|
-
You can also import your own React components.
|
|
48
|
-
|
|
49
|
-
\`\`\`mdx
|
|
50
|
-
import { ExampleCard } from "./ExampleCard.tsx";
|
|
51
|
-
|
|
52
|
-
# Local import
|
|
53
|
-
|
|
54
|
-
Use a mix of markdown and components.
|
|
55
|
-
|
|
56
|
-
<ExampleCard title="Local import">
|
|
57
|
-
Your own React component.
|
|
58
|
-
</ExampleCard>
|
|
59
|
-
\`\`\`
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Export
|
|
64
|
-
|
|
65
|
-
- Run \`npx supadeck export\`
|
|
66
|
-
- The deck is rendered as HTML first
|
|
67
|
-
- Then exported to PDF with headless Chromium
|
|
68
|
-
`;
|
|
69
|
-
}
|
package/dist/cli/workspace.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { starterDeckTemplate } from './templates.js';
|
|
4
|
-
export function resolveDeckPath(input, cwd = process.cwd(), defaultInput) {
|
|
5
|
-
const resolvedInput = input ?? defaultInput;
|
|
6
|
-
if (!resolvedInput) {
|
|
7
|
-
return path.resolve(cwd, 'deck.mdx');
|
|
8
|
-
}
|
|
9
|
-
const candidate = path.resolve(cwd, resolvedInput);
|
|
10
|
-
if (path.extname(candidate)) {
|
|
11
|
-
return candidate;
|
|
12
|
-
}
|
|
13
|
-
return path.join(candidate, 'deck.mdx');
|
|
14
|
-
}
|
|
15
|
-
async function writeFileIfMissing(filePath, contents) {
|
|
16
|
-
try {
|
|
17
|
-
await fs.access(filePath);
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
21
|
-
await fs.writeFile(filePath, contents, 'utf8');
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
export async function ensureStarterDeck(deckPath) {
|
|
25
|
-
await writeFileIfMissing(deckPath, starterDeckTemplate());
|
|
26
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import matter from 'gray-matter';
|
|
2
|
-
import { unified } from 'unified';
|
|
3
|
-
import remarkGfm from 'remark-gfm';
|
|
4
|
-
import remarkParse from 'remark-parse';
|
|
5
|
-
import remarkMdx from 'remark-mdx';
|
|
6
|
-
const parser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
|
|
7
|
-
const DEFAULT_CONFIG = {
|
|
8
|
-
title: 'Untitled deck',
|
|
9
|
-
theme: 'default',
|
|
10
|
-
aspectRatio: '16:9',
|
|
11
|
-
showSlideNumbers: true,
|
|
12
|
-
transition: 'fade'
|
|
13
|
-
};
|
|
14
|
-
function toOffset(point) {
|
|
15
|
-
return point?.offset ?? 0;
|
|
16
|
-
}
|
|
17
|
-
function asString(value, fallback) {
|
|
18
|
-
return typeof value === 'string' && value.trim() ? value : fallback;
|
|
19
|
-
}
|
|
20
|
-
function asBoolean(value, fallback) {
|
|
21
|
-
return typeof value === 'boolean' ? value : fallback;
|
|
22
|
-
}
|
|
23
|
-
function asPositiveInteger(value) {
|
|
24
|
-
const parsed = typeof value === 'number'
|
|
25
|
-
? value
|
|
26
|
-
: typeof value === 'string' && value.trim()
|
|
27
|
-
? Number(value)
|
|
28
|
-
: Number.NaN;
|
|
29
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
return parsed;
|
|
33
|
-
}
|
|
34
|
-
function normalizeDeckSections(value) {
|
|
35
|
-
if (!Array.isArray(value)) {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
const sections = value.flatMap((entry) => {
|
|
39
|
-
if (!entry || typeof entry !== 'object') {
|
|
40
|
-
return [];
|
|
41
|
-
}
|
|
42
|
-
const label = asString(entry.label, '');
|
|
43
|
-
const start = asPositiveInteger(entry.start);
|
|
44
|
-
const end = asPositiveInteger(entry.end ?? entry.start);
|
|
45
|
-
if (!label || start === null || end === null || end < start) {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
return [
|
|
49
|
-
{
|
|
50
|
-
label,
|
|
51
|
-
start: start - 1,
|
|
52
|
-
end: end - 1
|
|
53
|
-
}
|
|
54
|
-
];
|
|
55
|
-
});
|
|
56
|
-
return sections.length > 0 ? sections : undefined;
|
|
57
|
-
}
|
|
58
|
-
function sanitizeDeckSections(sections, totalSlides) {
|
|
59
|
-
if (!sections || totalSlides <= 0) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
const sanitized = sections.flatMap((section) => {
|
|
63
|
-
const start = Math.min(Math.max(section.start, 0), totalSlides - 1);
|
|
64
|
-
const end = Math.min(Math.max(section.end, start), totalSlides - 1);
|
|
65
|
-
if (end < start) {
|
|
66
|
-
return [];
|
|
67
|
-
}
|
|
68
|
-
return [{ ...section, start, end }];
|
|
69
|
-
});
|
|
70
|
-
return sanitized.length > 0 ? sanitized : undefined;
|
|
71
|
-
}
|
|
72
|
-
export function normalizeDeckConfig(frontmatter = {}, themeOverride) {
|
|
73
|
-
return {
|
|
74
|
-
title: asString(frontmatter.title, DEFAULT_CONFIG.title),
|
|
75
|
-
theme: themeOverride ?? asString(frontmatter.theme, DEFAULT_CONFIG.theme),
|
|
76
|
-
aspectRatio: asString(frontmatter.aspectRatio, DEFAULT_CONFIG.aspectRatio),
|
|
77
|
-
showSlideNumbers: asBoolean(frontmatter.showSlideNumbers, DEFAULT_CONFIG.showSlideNumbers),
|
|
78
|
-
transition: asString(frontmatter.transition, DEFAULT_CONFIG.transition),
|
|
79
|
-
sections: normalizeDeckSections(frontmatter.sections)
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
export function parseDeck(source, options = {}) {
|
|
83
|
-
const matterFile = matter(source);
|
|
84
|
-
const content = matterFile.content.replace(/^\s+/, '');
|
|
85
|
-
const tree = parser.parse(content);
|
|
86
|
-
const children = tree.children ?? [];
|
|
87
|
-
let preludeEnd = 0;
|
|
88
|
-
let nodeIndex = 0;
|
|
89
|
-
while (nodeIndex < children.length && children[nodeIndex]?.type === 'mdxjsEsm') {
|
|
90
|
-
preludeEnd = toOffset(children[nodeIndex].position?.end);
|
|
91
|
-
nodeIndex += 1;
|
|
92
|
-
}
|
|
93
|
-
const prelude = content.slice(0, preludeEnd).trim();
|
|
94
|
-
const slideBoundaries = [];
|
|
95
|
-
for (const node of children.slice(nodeIndex)) {
|
|
96
|
-
if (node.type === 'thematicBreak') {
|
|
97
|
-
slideBoundaries.push({
|
|
98
|
-
start: toOffset(node.position?.start),
|
|
99
|
-
end: toOffset(node.position?.end)
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const slides = [];
|
|
104
|
-
let cursor = preludeEnd;
|
|
105
|
-
if (slideBoundaries.length === 0) {
|
|
106
|
-
const body = content.slice(cursor).trim();
|
|
107
|
-
if (body) {
|
|
108
|
-
slides.push({ index: 0, body });
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
for (const boundary of slideBoundaries) {
|
|
113
|
-
const body = content.slice(cursor, boundary.start).trim();
|
|
114
|
-
if (body) {
|
|
115
|
-
slides.push({ index: slides.length, body });
|
|
116
|
-
}
|
|
117
|
-
cursor = boundary.end;
|
|
118
|
-
}
|
|
119
|
-
const trailing = content.slice(cursor).trim();
|
|
120
|
-
if (trailing) {
|
|
121
|
-
slides.push({ index: slides.length, body: trailing });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (slides.length === 0) {
|
|
125
|
-
slides.push({ index: 0, body: '# Untitled slide' });
|
|
126
|
-
}
|
|
127
|
-
const config = normalizeDeckConfig(matterFile.data, options.themeOverride);
|
|
128
|
-
config.sections = sanitizeDeckSections(config.sections, slides.length);
|
|
129
|
-
return {
|
|
130
|
-
frontmatter: matterFile.data ?? {},
|
|
131
|
-
config,
|
|
132
|
-
prelude,
|
|
133
|
-
slides,
|
|
134
|
-
rawContent: content
|
|
135
|
-
};
|
|
136
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
const PARAGRAPHLESS_TAGS = new Set([
|
|
2
|
-
'abbr',
|
|
3
|
-
'b',
|
|
4
|
-
'button',
|
|
5
|
-
'cite',
|
|
6
|
-
'code',
|
|
7
|
-
'data',
|
|
8
|
-
'dfn',
|
|
9
|
-
'em',
|
|
10
|
-
'h1',
|
|
11
|
-
'h2',
|
|
12
|
-
'h3',
|
|
13
|
-
'h4',
|
|
14
|
-
'h5',
|
|
15
|
-
'h6',
|
|
16
|
-
'kbd',
|
|
17
|
-
'label',
|
|
18
|
-
'legend',
|
|
19
|
-
'mark',
|
|
20
|
-
'output',
|
|
21
|
-
'p',
|
|
22
|
-
'pre',
|
|
23
|
-
'q',
|
|
24
|
-
'rp',
|
|
25
|
-
'rt',
|
|
26
|
-
'ruby',
|
|
27
|
-
's',
|
|
28
|
-
'samp',
|
|
29
|
-
'small',
|
|
30
|
-
'span',
|
|
31
|
-
'strong',
|
|
32
|
-
'sub',
|
|
33
|
-
'summary',
|
|
34
|
-
'sup',
|
|
35
|
-
'time',
|
|
36
|
-
'u',
|
|
37
|
-
'var'
|
|
38
|
-
]);
|
|
39
|
-
function shouldUnwrapParagraphChildren(node) {
|
|
40
|
-
return ((node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
|
|
41
|
-
typeof node.name === 'string' &&
|
|
42
|
-
PARAGRAPHLESS_TAGS.has(node.name.toLowerCase()));
|
|
43
|
-
}
|
|
44
|
-
function unwrapParagraphChildren(children) {
|
|
45
|
-
return children.flatMap((child) => (child.type === 'paragraph' ? child.children ?? [] : [child]));
|
|
46
|
-
}
|
|
47
|
-
function visit(node) {
|
|
48
|
-
const children = node.children;
|
|
49
|
-
if (!children) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
if (shouldUnwrapParagraphChildren(node)) {
|
|
53
|
-
node.children = unwrapParagraphChildren(children);
|
|
54
|
-
}
|
|
55
|
-
for (const child of node.children ?? []) {
|
|
56
|
-
visit(child);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
export function remarkUnwrapJsxParagraphs() {
|
|
60
|
-
return (tree) => {
|
|
61
|
-
visit(tree);
|
|
62
|
-
};
|
|
63
|
-
}
|
package/dist/export/pdf.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { chromium } from "playwright";
|
|
4
|
-
export async function exportDeckToPdf({ url, outputPath, }) {
|
|
5
|
-
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
6
|
-
let browser;
|
|
7
|
-
try {
|
|
8
|
-
browser = await chromium.launch({ headless: true });
|
|
9
|
-
}
|
|
10
|
-
catch (error) {
|
|
11
|
-
throw new Error(`Chromium is not available for PDF export. Run "npx playwright install chromium" and try again.\n\nOriginal error: ${error instanceof Error ? error.message : String(error)}`);
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
const page = await browser.newPage({
|
|
15
|
-
viewport: {
|
|
16
|
-
width: 1600,
|
|
17
|
-
height: 900,
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
await page.goto(url, { waitUntil: "networkidle" });
|
|
21
|
-
await page.waitForFunction(() => window.__SUPADECK_READY__ === true);
|
|
22
|
-
await page.emulateMedia({ media: "screen" });
|
|
23
|
-
await page.pdf({
|
|
24
|
-
path: outputPath,
|
|
25
|
-
landscape: true,
|
|
26
|
-
printBackground: true,
|
|
27
|
-
preferCSSPageSize: false,
|
|
28
|
-
format: "A4",
|
|
29
|
-
margin: {
|
|
30
|
-
top: "0",
|
|
31
|
-
right: "0",
|
|
32
|
-
bottom: "0",
|
|
33
|
-
left: "0",
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
finally {
|
|
38
|
-
await browser?.close();
|
|
39
|
-
}
|
|
40
|
-
}
|
package/dist/runtime/App.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo } from "react";
|
|
3
|
-
import { mergeComponents } from "./default-components.js";
|
|
4
|
-
import { useCurrentSlide } from "./hooks/slides.js";
|
|
5
|
-
import { DefaultDeck } from "./themes/base/DefaultDeck.js";
|
|
6
|
-
import { clamp, getHashIndex, parseAspectRatio, } from "./utils/use-current-slide.js";
|
|
7
|
-
const themeHelpers = {
|
|
8
|
-
clamp,
|
|
9
|
-
getHashIndex,
|
|
10
|
-
parseAspectRatio,
|
|
11
|
-
};
|
|
12
|
-
export function App({ deck, theme }) {
|
|
13
|
-
const slides = deck.slides ?? [];
|
|
14
|
-
const config = deck.config ?? {};
|
|
15
|
-
const [index, setIndex] = useCurrentSlide(slides.length);
|
|
16
|
-
const printMode = new URLSearchParams(window.location.search).get("print") === "1";
|
|
17
|
-
const components = useMemo(() => mergeComponents(theme), [theme]);
|
|
18
|
-
const DeckRenderer = theme.Deck ?? DefaultDeck;
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
document.title = config.title ?? "Supadeck";
|
|
21
|
-
window.__SUPADECK_READY__ = true;
|
|
22
|
-
return () => {
|
|
23
|
-
window.__SUPADECK_READY__ = false;
|
|
24
|
-
};
|
|
25
|
-
}, [config.title]);
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
const cleanup = theme.setup?.({
|
|
28
|
-
deck,
|
|
29
|
-
config,
|
|
30
|
-
slides,
|
|
31
|
-
currentIndex: index,
|
|
32
|
-
printMode,
|
|
33
|
-
components,
|
|
34
|
-
helpers: themeHelpers,
|
|
35
|
-
rootElement: document.documentElement,
|
|
36
|
-
});
|
|
37
|
-
return () => {
|
|
38
|
-
cleanup?.();
|
|
39
|
-
};
|
|
40
|
-
}, [theme, deck, config, slides, index, printMode, components]);
|
|
41
|
-
if (slides.length === 0) {
|
|
42
|
-
return _jsx("div", { className: "p-10 text-white", children: "No slides found." });
|
|
43
|
-
}
|
|
44
|
-
return (_jsx(DeckRenderer, { deck: deck, config: config, slides: slides, currentIndex: index, setCurrentIndex: setIndex, printMode: printMode, components: components, helpers: themeHelpers }));
|
|
45
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
function toneClasses(tone) {
|
|
3
|
-
if (tone === 'accent') {
|
|
4
|
-
return 'border-[color:var(--color-accent)] bg-[color:var(--color-accent)]/10';
|
|
5
|
-
}
|
|
6
|
-
if (tone === 'danger') {
|
|
7
|
-
return 'border-red-500/50 bg-red-500/10';
|
|
8
|
-
}
|
|
9
|
-
return 'border-[color:var(--color-border)] bg-white/60 dark:bg-white/5';
|
|
10
|
-
}
|
|
11
|
-
export function Callout({ children, tone = 'default' }) {
|
|
12
|
-
return (_jsx("div", { className: `rounded-[var(--radius-lg)] border px-6 py-5 text-xl shadow-lg shadow-black/5 backdrop-blur ${toneClasses(tone)}`, children: children }));
|
|
13
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Accordion } from '@base-ui-components/react/accordion';
|
|
3
|
-
export function Disclosure({ title, children }) {
|
|
4
|
-
return (_jsx(Accordion.Root, { className: "rounded-[var(--radius-lg)] border border-[color:var(--color-border)] bg-white/70 dark:bg-white/5", children: _jsxs(Accordion.Item, { value: "item-1", children: [_jsx(Accordion.Header, { children: _jsxs(Accordion.Trigger, { className: "flex w-full items-center justify-between px-5 py-4 text-left text-xl font-semibold", children: [_jsx("span", { children: title }), _jsx("span", { "aria-hidden": "true", children: "+" })] }) }), _jsx(Accordion.Panel, { className: "px-5 pb-5 text-lg text-[color:var(--color-foreground)]/80", children: children })] }) }));
|
|
5
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
export function Frame({ children }) {
|
|
3
|
-
return (_jsx("div", { className: "rounded-[var(--radius-xl)] border border-[color:var(--color-border)] bg-white/70 p-8 shadow-xl shadow-black/10 dark:bg-white/5", children: children }));
|
|
4
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Callout, Columns, Disclosure, Frame } from "./components/index.js";
|
|
3
|
-
import { Center } from "./components/Center.js";
|
|
4
|
-
function cx(...values) {
|
|
5
|
-
return values.filter(Boolean).join(" ");
|
|
6
|
-
}
|
|
7
|
-
export function createDefaultComponents() {
|
|
8
|
-
return {
|
|
9
|
-
h1: (props) => (_jsx("h1", { className: "text-6xl font-semibold tracking-tight text-balance", ...props })),
|
|
10
|
-
h2: (props) => (_jsx("h2", { className: "text-4xl font-semibold tracking-tight text-balance", ...props })),
|
|
11
|
-
h3: (props) => (_jsx("h3", { className: "text-2xl font-semibold tracking-tight", ...props })),
|
|
12
|
-
p: (props) => (_jsx("p", { className: "text-2xl leading-relaxed text-[color:var(--color-foreground)]/90", ...props })),
|
|
13
|
-
a: (props) => (_jsx("a", { className: "text-[color:var(--color-accent)] underline decoration-2 underline-offset-4", ...props })),
|
|
14
|
-
ul: (props) => (_jsx("ul", { className: "list-disc space-y-3 pl-8 text-2xl", ...props })),
|
|
15
|
-
ol: (props) => (_jsx("ol", { className: "list-decimal space-y-3 pl-8 text-2xl", ...props })),
|
|
16
|
-
li: (props) => (_jsx("li", { className: "pl-2", ...props })),
|
|
17
|
-
code: (props) => (_jsx("code", { className: "rounded-md bg-black/10 px-2 py-1 font-mono text-[0.9em] dark:bg-white/10", ...props })),
|
|
18
|
-
pre: (props) => (_jsx("pre", { className: "overflow-x-auto rounded-[var(--radius-lg)] border border-[color:var(--color-border)] bg-[color:var(--color-code-bg)] p-6 text-lg", ...props })),
|
|
19
|
-
table: ({ className, ...props }) => (_jsx("div", { className: "deck-table-wrap", children: _jsx("table", { className: cx("deck-table", className), ...props }) })),
|
|
20
|
-
thead: ({ className, ...props }) => (_jsx("thead", { className: cx("deck-thead", className), ...props })),
|
|
21
|
-
tbody: ({ className, ...props }) => (_jsx("tbody", { className: cx("deck-tbody", className), ...props })),
|
|
22
|
-
tr: ({ className, ...props }) => (_jsx("tr", { className: cx("deck-tr", className), ...props })),
|
|
23
|
-
th: ({ className, ...props }) => (_jsx("th", { className: cx("deck-th", className), ...props })),
|
|
24
|
-
td: ({ className, ...props }) => (_jsx("td", { className: cx("deck-td", className), ...props })),
|
|
25
|
-
blockquote: (props) => (_jsx("blockquote", { className: "border-l-4 border-[color:var(--color-accent)] pl-6 text-2xl italic", ...props })),
|
|
26
|
-
Callout,
|
|
27
|
-
Columns,
|
|
28
|
-
Disclosure,
|
|
29
|
-
Frame,
|
|
30
|
-
Center,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export function mergeComponents(theme) {
|
|
34
|
-
return {
|
|
35
|
-
...createDefaultComponents(),
|
|
36
|
-
...(theme?.components ?? {}),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import { clamp, getHashIndex } from "../utils/use-current-slide.js";
|
|
3
|
-
export function useCurrentSlide(total) {
|
|
4
|
-
const [index, setIndex] = useState(() => getHashIndex(total));
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
const onHashChange = () => setIndex(getHashIndex(total));
|
|
7
|
-
window.addEventListener("hashchange", onHashChange);
|
|
8
|
-
return () => window.removeEventListener("hashchange", onHashChange);
|
|
9
|
-
}, [total]);
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
const nextHash = `#${index + 1}`;
|
|
12
|
-
if (window.location.hash !== nextHash) {
|
|
13
|
-
history.replaceState(null, "", `${window.location.pathname}${window.location.search}${nextHash}`);
|
|
14
|
-
}
|
|
15
|
-
}, [index]);
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
const onKeyDown = (event) => {
|
|
18
|
-
if (event.metaKey || event.ctrlKey || event.altKey) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (["ArrowRight", "ArrowDown", "PageDown", " "].includes(event.key)) {
|
|
22
|
-
event.preventDefault();
|
|
23
|
-
setIndex((current) => clamp(current + 1, 0, total - 1));
|
|
24
|
-
}
|
|
25
|
-
if (["ArrowLeft", "ArrowUp", "PageUp"].includes(event.key)) {
|
|
26
|
-
event.preventDefault();
|
|
27
|
-
setIndex((current) => clamp(current - 1, 0, total - 1));
|
|
28
|
-
}
|
|
29
|
-
if (event.key === "Home") {
|
|
30
|
-
event.preventDefault();
|
|
31
|
-
setIndex(0);
|
|
32
|
-
}
|
|
33
|
-
if (event.key === "End") {
|
|
34
|
-
event.preventDefault();
|
|
35
|
-
setIndex(total - 1);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
window.addEventListener("keydown", onKeyDown);
|
|
39
|
-
return () => window.removeEventListener("keydown", onKeyDown);
|
|
40
|
-
}, [total]);
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
setIndex((current) => clamp(current, 0, Math.max(total - 1, 0)));
|
|
43
|
-
}, [total]);
|
|
44
|
-
return [index, setIndex];
|
|
45
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { SlideFrame } from './SlideFrame.js';
|
|
3
|
-
export function DeckSlide({ slide, config, total, index, printMode, components, }) {
|
|
4
|
-
const Slide = slide.Component;
|
|
5
|
-
return (_jsx(SlideFrame, { config: config, index: index, total: total, printMode: printMode, children: _jsx(Slide, { components: components }) }));
|
|
6
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { parseAspectRatio } from '../utils/use-current-slide.js';
|
|
3
|
-
export function SlideFrame({ children, config, index, total, printMode, }) {
|
|
4
|
-
const ratio = parseAspectRatio(config.aspectRatio);
|
|
5
|
-
return (_jsx("section", { className: `slide-frame ${printMode ? 'slide-frame-print' : ''}`, style: { '--slide-aspect-ratio': ratio }, "data-transition": config.transition, children: _jsxs("div", { className: "slide-surface", children: [_jsx("div", { className: "slide-content", children: children }), config.showSlideNumbers ? (_jsx("div", { className: "slide-footer", children: _jsxs("span", { children: [index + 1, " / ", total] }) })) : null] }) }));
|
|
6
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
export function createDefaultMdxComponents() {
|
|
3
|
-
return {
|
|
4
|
-
h1: (props) => (_jsx("h1", { className: "text-6xl font-semibold tracking-tight text-balance", ...props })),
|
|
5
|
-
h2: (props) => (_jsx("h2", { className: "text-4xl font-semibold tracking-tight text-balance", ...props })),
|
|
6
|
-
h3: (props) => (_jsx("h3", { className: "text-2xl font-semibold tracking-tight", ...props })),
|
|
7
|
-
p: (props) => (_jsx("p", { className: "text-2xl leading-relaxed text-[color:var(--color-foreground)]/90", ...props })),
|
|
8
|
-
a: (props) => (_jsx("a", { className: "text-[color:var(--color-accent)] underline decoration-2 underline-offset-4", ...props })),
|
|
9
|
-
ul: (props) => (_jsx("ul", { className: "list-disc space-y-3 pl-8 text-2xl", ...props })),
|
|
10
|
-
ol: (props) => (_jsx("ol", { className: "list-decimal space-y-3 pl-8 text-2xl", ...props })),
|
|
11
|
-
li: (props) => (_jsx("li", { className: "pl-2", ...props })),
|
|
12
|
-
code: (props) => (_jsx("code", { className: "rounded-md bg-black/10 px-2 py-1 font-mono text-[0.9em] dark:bg-white/10", ...props })),
|
|
13
|
-
pre: (props) => (_jsx("pre", { className: "overflow-x-auto rounded-[var(--radius-lg)] border border-[color:var(--color-border)] bg-[color:var(--color-code-bg)] p-6 text-lg", ...props })),
|
|
14
|
-
blockquote: (props) => (_jsx("blockquote", { className: "border-l-4 border-[color:var(--color-accent)] pl-6 text-2xl italic", ...props })),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=mdx-components.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { DeckProgress } from './DeckProgress.js';
|
|
3
|
-
import { DeckTitle } from './DeckTitle.js';
|
|
4
|
-
export function DeckChrome({ title, currentIndex, total }) {
|
|
5
|
-
return (_jsxs("div", { className: "deck-chrome", children: [_jsx(DeckTitle, { title: title }), _jsx(DeckProgress, { currentIndex: currentIndex, total: total })] }));
|
|
6
|
-
}
|