-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: PDF handout (slides on top, notes on bottom of page) #1421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8f070b6
d37e759
b1e4dab
0449640
0a037d6
f4f1213
c8aa976
eac5d2f
dff0ee1
255dc6e
12c55cc
3107954
c2d0208
78317aa
dbe8dd6
3e20b97
3d39388
6275f22
09cecab
d90ed29
434df02
5b931c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<script setup lang="ts"> | ||
|
||
</script> | ||
|
||
<template> | ||
<div class="print w-full absolute bottom-[75px]"> | ||
<div class="h-[1px] bg-black w-full" /> | ||
<div class="mt-2 relative"> | ||
<div class="top-3.0 absolute left-0 !text-[11px]"> | ||
© {{ new Date().getFullYear() }} Slidev | ||
</div> | ||
<div class="top-3.0 absolute right-0 !text-[11px]"> | ||
Presentation slides for developers | ||
</div> | ||
</div> | ||
</div> | ||
|
||
</template> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<template> | ||
<div> | ||
<div class="break-after-page"> | ||
<span class="font-bold text-xl border-b-4 border-r-4 px-1 py-1 border-gray-800 "> | ||
SLIDEV | ||
</span> | ||
<div class="h-56" /> | ||
<div class="max-w-md w-full mx-auto"> | ||
<div class="w-120 mx-auto font-bold flex flex-wrap gap-4"> | ||
<h1 class="text-5xl font-bold border-b-4 border-gray-800"> | ||
Welcome to Slidev | ||
</h1> | ||
v1.0 | ||
</div> | ||
<div class="h-32" /> | ||
<div class="text-2xl font-bold mx-auto w-120 text-right"> | ||
Slidev Starter Template | ||
</div> | ||
</div> | ||
</div> | ||
<div class="break-after-page mt-68 "> | ||
<div class="font-bold text-xl mt-8"> | ||
COPYRIGHT | ||
</div> | ||
<div class="flex flex-row gap-4 mt-4 align-center items-center"> | ||
© {{ new Date().getFullYear().toString() }} Slidev. | ||
</div> | ||
<p class="mt-4"> | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
</p> | ||
<p class="mt-4"> | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
</p> | ||
<p class="mt-4"> | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
</p> | ||
</div> | ||
</div> | ||
</template> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<script setup lang="ts"> | ||
import { parseRangeString } from '@slidev/parser/core' | ||
import { useNav } from '../composables/useNav' | ||
import PrintHandout from './PrintHandout.vue' | ||
|
||
const { slides, currentRoute } = useNav() | ||
|
||
// In print mode, the routes will never change. So we don't need reactivity here. | ||
let routes = slides.value | ||
if (currentRoute.value.query.range) { | ||
const r = parseRangeString(routes.length, currentRoute.value.query.range as string) | ||
routes = r.map(i => routes[i - 1]) | ||
} | ||
</script> | ||
|
||
<template> | ||
<div id="print-container"> | ||
<div id="print-content"> | ||
<PrintHandout v-for="(route, index) of routes" :key="route.no" :route="route" :index="index" /> | ||
<div class="break-after-page h-130" /> | ||
</div> | ||
<slot name="controls" /> | ||
</div> | ||
</template> | ||
|
||
<style lang="postcss"> | ||
#print-content { | ||
@apply bg-main; | ||
} | ||
.print-slide-container { | ||
@apply relative overflow-hidden break-after-page translate-0; | ||
} | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<script setup lang="ts"> | ||
import type { SlideRoute } from '@slidev/types' | ||
import { computed } from 'vue' | ||
import NoteDisplay from './NoteDisplay.vue' | ||
import { HandoutBottom } from '#slidev/global-layers' | ||
|
||
const props = defineProps<{ | ||
route: SlideRoute | ||
index: number | ||
}>() | ||
const route = computed(() => props.route) | ||
</script> | ||
|
||
<template> | ||
<div class="break-after-page"> | ||
<!-- A4 specific, figure out better customization --> | ||
<div class="w-full mt-104 h-176 flex flex-col relative overflow-hidden"> | ||
<NoteDisplay | ||
v-if="route.meta?.slide!.noteHTML" :note-html="route.meta?.slide!.noteHTML" | ||
class="w-full mx-auto px-2 handout-notes" | ||
/> | ||
|
||
<div class=""> | ||
<HandoutBottom :page-number="index + 100" /> | ||
<!-- I would like to do this in HandoutBottom, but somehow props don't get passed. --> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can remove the hard-coded page numbering then |
||
<div class="absolute bottom-5 right-12 text-right text-[11px]"> | ||
{{ index + 1 }} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style scoped> | ||
.handout-notes { | ||
@apply max-w-186; | ||
/* Overwrite if necessary: A4 specific */ | ||
} | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<script setup lang="ts"> | ||
import { watchEffect } from 'vue' | ||
import { useNav } from '../../composables/useNav' | ||
import { themeVars } from '../../env' | ||
import { HandoutCover } from '#slidev/global-layers' | ||
|
||
const { isPrintMode } = useNav() | ||
|
||
watchEffect(() => { | ||
const html = document.body.parentNode as HTMLElement | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think maybe we don't need a |
||
if (isPrintMode.value) | ||
html.classList.add('print') | ||
else | ||
html.classList.remove('print') | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div id="page-root" class="grid grid-cols-[1fr_max-content]" :style="themeVars"> | ||
<HandoutCover /> | ||
</div> | ||
</template> | ||
|
||
<style> | ||
html.print, | ||
html.print body, | ||
html.print #app { | ||
height: auto; | ||
overflow: auto; | ||
} | ||
|
||
html.print #page-root { | ||
height: auto; | ||
overflow: hidden; | ||
} | ||
|
||
html.print * { | ||
-webkit-print-color-adjust: exact; | ||
} | ||
|
||
html.print { | ||
width: 100%; | ||
height: 100%; | ||
overflow: visible; | ||
} | ||
|
||
html.print body { | ||
margin: 0 auto; | ||
border: 0; | ||
padding: 0; | ||
float: none; | ||
overflow: visible; | ||
} | ||
|
||
@page { | ||
size: A4; | ||
margin-top: 1cm; | ||
margin-bottom: 1cm; | ||
margin-left: 1.5cm; | ||
margin-right: 1.5cm; | ||
} | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<script setup lang="ts"> | ||
import { watchEffect } from 'vue' | ||
import { windowSize } from '../../state' | ||
import { useNav } from '../../composables/useNav' | ||
import { themeVars } from '../../env' | ||
import PrintContainerHandout from '../../internals/PrintContainerHandout.vue' | ||
|
||
const { isPrintMode } = useNav() | ||
|
||
watchEffect(() => { | ||
const html = document.body.parentNode as HTMLElement | ||
if (isPrintMode.value) | ||
html.classList.add('print') | ||
else | ||
html.classList.remove('print') | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div id="page-root" class="grid grid-cols-[1fr_max-content]" :style="themeVars"> | ||
<PrintContainerHandout class="w-full h-full" :width="windowSize.width.value" /> | ||
</div> | ||
</template> | ||
|
||
<style> | ||
html.print, | ||
html.print body, | ||
html.print #app { | ||
height: auto; | ||
overflow: auto; | ||
} | ||
html.print #page-root { | ||
height: auto; | ||
overflow: hidden; | ||
} | ||
html.print * { | ||
-webkit-print-color-adjust: exact; | ||
} | ||
html.print { | ||
width: 100%; | ||
height: 100%; | ||
overflow: visible; | ||
} | ||
html.print body { | ||
margin: 0 auto; | ||
border: 0; | ||
padding: 0; | ||
float: none; | ||
overflow: visible; | ||
} | ||
|
||
@page { | ||
size: A4; | ||
margin-top: 0cm; | ||
margin-bottom: 0cm; | ||
margin-left: 0cm; | ||
margin-right: 0cm; | ||
} | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,6 +61,16 @@ export default function setupRoutes() { | |
path: '/presenter/print', | ||
component: () => import('../pages/presenter/print.vue'), | ||
}, | ||
{ | ||
name: 'handout', | ||
path: '/handout', | ||
component: () => import('../pages/handout/print.vue'), | ||
}, | ||
{ | ||
name: 'cover', | ||
path: '/cover', | ||
component: () => import('../pages/cover/print.vue'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this component be in |
||
}, | ||
) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,8 @@ const CONFIG_RESTART_FIELDS: (keyof SlidevConfig)[] = [ | |
const FILES_CREATE_RESTART = [ | ||
'global-bottom.vue', | ||
'global-top.vue', | ||
'handout-bottom.vue', | ||
'handout-cover.vue', | ||
'uno.config.js', | ||
'uno.config.ts', | ||
'unocss.config.js', | ||
|
@@ -603,6 +605,14 @@ function exportOptions<T>(args: Argv<T>) { | |
type: 'boolean', | ||
describe: 'export png pages without the default browser background', | ||
}) | ||
.option('cover', { | ||
type: 'boolean', | ||
describe: 'prepend cover to handout, needs handout-cover.vue in project', | ||
}) | ||
Comment on lines
+608
to
+611
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose we don't need this option? We could do it automatically when |
||
.option('handout', { | ||
type: 'boolean', | ||
describe: 'Export handout with slides on top and notes on bottom, optionally prepending a cover', | ||
}) | ||
Comment on lines
+612
to
+615
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
function printInfo( | ||
|
Uh oh!
There was an error while loading. Please reload this page.