Skip to content

Commit ce4f91d

Browse files
committed
feat: allow SPA to download PDF
1 parent 5fad6ee commit ce4f91d

File tree

9 files changed

+140
-10
lines changed

9 files changed

+140
-10
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"devDependencies": {
1616
"@antfu/eslint-config": "^0.6.4",
1717
"@antfu/utils": "^0.1.1",
18+
"@types/file-saver": "^2.0.2",
1819
"@types/jest": "^26.0.23",
1920
"@types/node": "^15.0.1",
2021
"@types/prettier": "^2.2.3",

packages/client/internals/NavControls.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { computed, defineProps } from 'vue'
44
import { isDark, toggleDark } from '../logic/dark'
55
import { hasNext, hasPrev, prev, next, isPresenter, currentPage } from '../logic/nav'
66
import { showOverview, showEditor } from '../state'
7+
import { configs } from '../env'
78
import RecordingControls from './RecordingControls.vue'
89
910
defineProps({
@@ -16,6 +17,11 @@ const { isFullscreen, toggle: toggleFullscreen } = useFullscreen(document.body)
1617
1718
const presenterLink = computed(() => `${location.origin}/presenter/${currentPage.value}`)
1819
const nonPresenterLink = computed(() => `${location.origin}/${currentPage.value}`)
20+
21+
function downloadPDF() {
22+
import('file-saver')
23+
.then(i => i.saveAs(`${import.meta.env.BASE_URL}slidev-exported.pdf`, `${configs.title}.pdf`))
24+
}
1925
</script>
2026

2127
<template>
@@ -60,5 +66,10 @@ const nonPresenterLink = computed(() => `${location.origin}/${currentPage.value}
6066

6167
<RecordingControls v-if="!isPresenter" />
6268
</template>
69+
<template v-else>
70+
<button v-if="configs.allowDownload" class="icon-btn" @click="downloadPDF">
71+
<carbon:download />
72+
</button>
73+
</template>
6374
</nav>
6475
</template>

packages/client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@vueuse/core": "^4.9.0",
1818
"@vueuse/head": "^0.5.1",
1919
"codemirror": "^5.61.0",
20+
"file-saver": "^2.0.5",
2021
"js-base64": "^3.6.0",
2122
"js-yaml": "^3.14.1",
2223
"monaco-editor": "^0.23.0",

packages/slidev/node/build.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { promises as fs } from 'fs'
2-
import { resolve } from 'path'
3-
import { build as viteBuild, InlineConfig, mergeConfig } from 'vite'
2+
import { resolve, join } from 'path'
3+
import http from 'http'
4+
import { build as viteBuild, InlineConfig, mergeConfig, ResolvedConfig } from 'vite'
5+
import connect from 'connect'
6+
import sirv from 'sirv'
47
import { ViteSlidevPlugin } from './plugins/preset'
58
import { getIndexHtml } from './common'
69
import { ResolvedSlidevOptions, SlidevPluginOptions } from './plugins/options'
10+
import { exportSlides } from './export'
711

812
export async function build(
913
options: ResolvedSlidevOptions,
@@ -12,13 +16,21 @@ export async function build(
1216
) {
1317
const indexPath = resolve(options.userRoot, 'index.html')
1418
await fs.writeFile(indexPath, await getIndexHtml(options), 'utf-8')
19+
let config: ResolvedConfig = undefined!
20+
1521
try {
1622
await viteBuild(
1723
mergeConfig(
1824
viteConfig,
1925
<InlineConfig>({
2026
plugins: [
2127
ViteSlidevPlugin(options, pluginOptions),
28+
{
29+
name: 'resolve-config',
30+
configResolved(_config) {
31+
config = _config
32+
},
33+
},
2234
],
2335
}),
2436
),
@@ -27,4 +39,25 @@ export async function build(
2739
finally {
2840
await fs.unlink(indexPath)
2941
}
42+
43+
if (options.data.config.allowDownload) {
44+
const port = 12445
45+
const app = connect()
46+
const server = http.createServer(app)
47+
app.use(
48+
config.base,
49+
sirv(config.build.outDir, {
50+
etag: true,
51+
single: true,
52+
}),
53+
)
54+
server.listen(port)
55+
await exportSlides({
56+
port,
57+
total: options.data.slides.length,
58+
format: 'pdf',
59+
output: join(config.build.outDir, 'slidev-exported.pdf'),
60+
})
61+
server.close()
62+
}
3063
}

packages/slidev/node/parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export function parse(
120120
config.title ??= headmatter.title ?? (slides[0].content.match(/^# (.*)$/m)?.[1] || '').trim()
121121
config.remoteAssets ??= headmatter.remoteAssets ?? true
122122
config.monaco ??= headmatter.monaco ?? 'dev'
123+
config.allowDownload ??= headmatter.allowDownload ?? true
123124

124125
return {
125126
raw: markdown,

packages/slidev/node/plugins/preset.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export function ViteSlidevPlugin(
144144
ext: '.png',
145145
},
146146
],
147-
resolveMode: '@fs',
147+
resolveMode: id => id.endsWith('index.html') ? 'relative' : '@fs',
148148
awaitDownload: mode === 'build',
149149
...remoteAssetsOptions,
150150
})

packages/slidev/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@vue/compiler-sfc": "^3.0.11",
5353
"cli-progress": "^3.9.0",
5454
"codemirror": "^5.61.0",
55+
"connect": "^3.7.0",
5556
"enquirer": "^2.3.6",
5657
"fast-glob": "^3.2.5",
5758
"fs-extra": "^9.1.0",
@@ -63,6 +64,7 @@
6364
"markdown-it-prism": "^2.1.6",
6465
"monaco-editor": "^0.23.0",
6566
"pdf-lib": "^1.16.0",
67+
"sirv": "^1.0.11",
6668
"vite": "^2.2.3",
6769
"vite-plugin-components": "^0.8.4",
6870
"vite-plugin-icons": "^0.5.0",
@@ -78,6 +80,7 @@
7880
"@antfu/ni": "^0.5.8",
7981
"@types/cli-progress": "^3.9.1",
8082
"@types/codemirror": "^0.0.109",
83+
"@types/connect": "^3.4.34",
8184
"@types/fs-extra": "^9.0.11",
8285
"@types/js-yaml": "^3",
8386
"@types/markdown-it": "^12.0.1",

packages/types/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export interface SlidevConfig {
2525
* @defult 'dev'
2626
*/
2727
monaco: boolean | 'dev'
28+
/**
29+
* @default true
30+
*/
31+
allowDownload: boolean
2832
}
2933

3034
export interface SlidevMarkdown {

0 commit comments

Comments
 (0)