Skip to content
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

feat: print templates, slide layers and rewrite export function #1513

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7f03f74
feat: custom page templates
KermanX Apr 10, 2024
1c237df
rewrite notes page
KermanX Apr 11, 2024
604bee4
fix: overview style
KermanX Apr 11, 2024
ef9c67c
feat: implement print template
KermanX Apr 14, 2024
0c235e9
feat: implement entry template
KermanX Apr 14, 2024
0f6c200
Merge branch 'main' into feat/custom-pages
KermanX Apr 14, 2024
8cf763c
fix: print styles
KermanX Apr 14, 2024
6d326db
fix: warnings
KermanX Apr 14, 2024
05e7b7a
fix: e2e test
KermanX Apr 14, 2024
98f3bc9
feat: implement slide top & bottom layer
KermanX Apr 14, 2024
3f1dac5
Merge branch 'main' into feat/custom-pages
KermanX Apr 16, 2024
06d2e72
fix: restore id
KermanX Apr 16, 2024
8ff21ca
Merge branch 'main' into feat/custom-pages
KermanX Apr 16, 2024
4ed3e8b
fix
KermanX Apr 16, 2024
5112946
chore: update
KermanX Apr 17, 2024
3b491b3
docs: fix dead link
KermanX Apr 17, 2024
0587c2d
Merge branch 'main' into feat/custom-pages
KermanX Apr 17, 2024
cecfced
fix
KermanX Apr 17, 2024
6428d19
Merge branch 'feat/custom-pages' of https://github.com/KermanX/slidev…
KermanX Apr 17, 2024
c567e87
Merge branch 'main' into feat/custom-pages
KermanX Apr 18, 2024
dec41e1
feat: handout print template (wip)
KermanX Apr 18, 2024
8b8637b
chore: update
KermanX Apr 19, 2024
869da0e
chore: update
KermanX Apr 19, 2024
c6c151d
wip: rewrite export function
KermanX Apr 19, 2024
152d6fd
chore: update
KermanX Apr 19, 2024
408f0e3
wip
KermanX Apr 20, 2024
75f0744
Merge branch 'main' into feat/custom-pages
KermanX May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions cypress/e2e/examples/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,22 @@ context('Basic', () => {
cy.contains('Global Footer')
.should('exist')

cy.get('#page-root > #slide-container > #slide-content')

cy.rightArrow()
.url()
.should('eq', `${BASE}/2`)

cy.contains('Global Footer')
.should('not.exist')

cy.get('#page-root > #slide-container > #slide-content > #slideshow .slidev-page-2 > div > p')
cy.get('#slideshow .slidev-page-2 > div > p')
.should('have.css', 'border-color', 'rgb(0, 128, 0)')
.should('not.have.css', 'color', 'rgb(128, 0, 0)')

goPage(5)

cy.get('#page-root > #slide-container > #slide-content > #slideshow .slidev-page-5 .slidev-code')
cy.get('#slideshow .slidev-page-5 .slidev-code')
.should('have.text', '<div>{{$slidev.nav.currentPage}}</div>')
.get('#page-root > #slide-container > #slide-content > #slideshow .slidev-page-5 > div > p')
.get('#slideshow .slidev-page-5 > div > p')
.should('have.text', 'Current Page: 5')
})

Expand Down
2 changes: 1 addition & 1 deletion demo/starter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"build": "slidev build",
"dev": "nodemon -w '../../packages/slidev/dist/*.mjs' --exec \"slidev ./slides.md --open=false --log=info --inspect\"",
"export": "slidev export",
"export-notes": "slidev export-notes"
"export-notes": "slidev export -t notes"
},
"devDependencies": {
"@slidev/cli": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion demo/vue-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"build": "slidev build",
"dev": "nodemon -w '../../packages/slidev/dist/*.mjs' --exec \"slidev ./slides.md --open=false --log=info --inspect\"",
"export": "slidev export",
"export-notes": "slidev export-notes"
"export-notes": "slidev export -t notes"
},
"devDependencies": {
"@slidev/cli": "workspace:*",
Expand Down
16 changes: 10 additions & 6 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ const Customizations: (DefaultTheme.NavItemWithLink | DefaultTheme.NavItemChildr
link: '/custom/directory-structure',
},
{
text: 'Fonts',
link: '/custom/fonts',
text: 'Vue Global Context',
link: '/custom/vue-context',
},
{
text: 'Global Layers',
link: '/custom/global-layers',
},
{
text: 'Highlighters',
Expand Down Expand Up @@ -165,12 +169,12 @@ const Customizations: (DefaultTheme.NavItemWithLink | DefaultTheme.NavItemChildr
link: '/custom/config-context-menu',
},
{
text: 'Vue Global Context',
link: '/custom/vue-context',
text: 'Custom Print Templates',
link: '/custom/print-templates',
},
{
text: 'Global Layers',
link: '/custom/global-layers',
text: 'Fonts',
link: '/custom/fonts',
},
]

Expand Down
6 changes: 4 additions & 2 deletions docs/custom/directory-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ your-slidev/
├── layouts/ # custom layouts
├── public/ # static assets
├── setup/ # custom setup / hooks
├── styles/ # custom style
├── styles/ # custom styles
├── snippets/ # code snippets
├── pages/print/ # custom print templates
├── index.html # injections to index.html
├── slides.md # the main slides entry
└── vite.config.ts # extending vite config
└── vite.config.ts # extending the vite config
```

All of them are optional.
Expand Down
16 changes: 10 additions & 6 deletions docs/custom/global-layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ Global layers allow you to have custom components that **persist** across slides

Slidev provides three layers for this usage, create `global-top.vue`, `global-bottom.vue` or `custom-nav-controls.vue` under your project root and it will pick up automatically.

There are also layers for **each** slide: `layouts/slide-top.vue` and `layouts/slide-bottom.vue`. The usage is similar to the global layers, but they are applied to every slide, so there may be more than one instance of them.

Layers relationship:

- Global Top (`global-top.vue`)
- Slide Top (`layouts/slide-top.vue`)
- Slides
- Slide Bottom (`layouts/slide-bottom.vue`)
- Global Bottom (`global-bottom.vue`)
- NavControls
- Customized Navigation Controls (`custom-nav-controls.vue`)
Expand All @@ -28,7 +32,7 @@ The text `Your Name` will appear on all your slides.
```html
<!-- custom-nav-controls -->
<template>
<button class="icon-btn" title="Next" @click="$slidev.nav.next">
<button class="icon-btn" title="Next" @click="$nav.next">
<carbon:arrow-right />
</button>
</template>
Expand All @@ -42,7 +46,7 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- hide the footer from Page 4 -->
<template>
<footer
v-if="$slidev.nav.currentPage !== 4"
v-if="$nav.currentPage !== 4"
class="absolute bottom-0 left-0 right-0 p-2"
>
Your Name
Expand All @@ -54,7 +58,7 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- hide the footer from "cover" layout -->
<template>
<footer
v-if="$slidev.nav.currentLayout !== 'cover'"
v-if="$nav.currentLayout !== 'cover'"
class="absolute bottom-0 left-0 right-0 p-2"
>
Your Name
Expand All @@ -66,10 +70,10 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- an example footer for pages -->
<template>
<footer
v-if="$slidev.nav.currentLayout !== 'cover'"
v-if="$nav.currentLayout !== 'cover'"
class="absolute bottom-0 left-0 right-0 p-2"
>
{{ $slidev.nav.currentPage }} / {{ $slidev.nav.total }}
{{ $nav.currentPage }} / {{ $nav.total }}
</footer>
</template>
```
Expand All @@ -78,7 +82,7 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- custom-nav-controls -->
<!-- hide the button in Presenter model -->
<template>
<button v-if="!$slidev.nav.isPresenter" class="icon-btn" title="Next" @click="$slidev.nav.next">
<button v-if="!$nav.isPresenter" class="icon-btn" title="Next" @click="$nav.next">
<carbon:arrow-right />
</button>
</template>
Expand Down
7 changes: 7 additions & 0 deletions docs/custom/print-templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Custom Print Templates

You can create custom print templates for your documents via custom print templates. To start, create a new file in the `./pages/print` directory, for example, `./pages/print/my-template.vue`.

To print your slides using the custom template, use the `--template` option in the CLI, more details in the [exporting guide](/guide/exporting#print-template).

To debug your print template, you can use the `--print-template` option in dev mode, and access `http://localhost:3030/print?print` to see the print preview.
31 changes: 17 additions & 14 deletions docs/guide/exporting.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ By default, Slidev exports one page per slide with clicks animations disabled. I
$ slidev export --with-clicks
```

### Print Template

By default, Slidev uses the `default` template to print your slides to PDF. You can also specify the print template with the `--template` (`-t`) option:

```bash
$ slidev export --template notes
```

Currently, Slidev provides the following templates:

- `default`: Slides only template. Each
- `notes`: Notes only.

But it is also possible to create your own print template. See [Custom Print Templates](/custom/print-templates) for details.

### Slide range

You can also specify a range of slides to export with the `--range` option:
Expand Down Expand Up @@ -104,29 +119,17 @@ The example above would export slides 1,6,7,8 and 10.
You can also export multiple slides at once:

```bash
$ slidev export slides1.md slides1.md
$ slidev export slides1.md slides2.md
```

Or

```bash
$ slidev export *.md
$ slidev export *.md # This based on your shell
```

In this case, each input file will generate its own PDf file.

## Presenter notes

> Available since v0.36.8

Export only the presenter notes (the last comment block for each slide) into a text document in PDF:

```bash
$ slidev export-notes
```

This command also accepts multiple entries like for the [export command](#multiple-entries)

## Single-Page Application (SPA)

See [Static Hosting](/guide/hosting).
Expand Down
4 changes: 3 additions & 1 deletion docs/guide/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ Options:
- `--log` (`'error', 'warn', 'info', 'silent'`, default: `'warn'`): Log level.
- `--force`, `-f` (`boolean`, default: `false`): force the optimizer to ignore the cache and re-bundle.
- `--theme`, `-t` (`string`): override theme.
- `--print-template` (`string`): specify [the print template](/guide/exporting#print-template).

### `slidev build [entry]`

Expand Down Expand Up @@ -202,7 +203,8 @@ Options:
- `--range` (`string`): page ranges to export (example: `'1,4-5,6'`).
- `--dark` (`boolean`, default: `false`): export as dark theme.
- `--with-clicks`, `-c` (`boolean`, default: `false`): export pages for every click animation (see https://sli.dev/guide/animations.html#click-animations).
- `--theme`, `-t` (`string`): override theme.
- `--theme` (`string`): override theme.
- `--template`, `-t` (`string`, default: `default`): specify [the print template](/guide/exporting#print-template).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer --type with hard-coded options like slides notes handout - and let ppl to override them instead of creating arbitrary options. Because we might want to do some special handling for each type - over-generalize it might not be ideal for long term.

Copy link
Member Author

@KermanX KermanX Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is OK to allow users to create new print templates. This is because it will be the user/addon's responsibility to handle other options like --with-clicks or so. Otherwise, users are forced to override one of the 3 templates even if there is no connection between the default one and the overridden one.

About the name of this option, should we change all "print templates" into "print types" or only the CLI flag?


### `slidev format [entry]`

Expand Down
9 changes: 8 additions & 1 deletion packages/client/composables/useNav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { RouteLocationNormalized, Router } from 'vue-router'
import { createSharedComposable } from '@vueuse/core'
import { logicOr } from '@vueuse/math'
import { clamp } from '@antfu/utils'
import { parseRangeString } from '@slidev/parser'
import { getCurrentTransition } from '../logic/transition'
import { getSlide, getSlidePath } from '../logic/slides'
import { CLICKS_MAX } from '../constants'
Expand Down Expand Up @@ -72,6 +73,7 @@ export interface SlidevContextNavState {
currentRoute: ComputedRef<RouteLocationNormalized>
isPrintMode: ComputedRef<boolean>
isPrintWithClicks: ComputedRef<boolean>
slidesToPrint: ComputedRef<SlideRoute[]>
isEmbedded: ComputedRef<boolean>
isPlaying: ComputedRef<boolean>
isPresenter: ComputedRef<boolean>
Expand Down Expand Up @@ -278,7 +280,11 @@ const useNavState = createSharedComposable((): SlidevContextNavState => {
return new URLSearchParams(location.search)
})
const isPrintMode = computed(() => query.value.has('print'))
const isPrintWithClicks = computed(() => query.value.get('print') === 'clicks')
const isPrintWithClicks = computed(() => query.value.get('clicks') === 'true')
const slidesToPrint = computed(() => {
const range = query.value.get('range')
return range ? parseRangeString(slides.value.length, range).map(i => slides.value[i - 1]) : slides.value
})
const isEmbedded = computed(() => query.value.has('embedded'))
const isPlaying = computed(() => currentRoute.value.name === 'play')
const isPresenter = computed(() => currentRoute.value.name === 'presenter')
Expand Down Expand Up @@ -348,6 +354,7 @@ const useNavState = createSharedComposable((): SlidevContextNavState => {
currentRoute,
isPrintMode,
isPrintWithClicks,
slidesToPrint,
isEmbedded,
isPlaying,
isPresenter,
Expand Down
11 changes: 11 additions & 0 deletions packages/client/composables/usePrintStyle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useStyleTag } from '@vueuse/core'
import { slideHeight, slideWidth } from '../env'

export function usePrintStyle() {
return useStyleTag(`
@page {
size: ${slideWidth}px ${slideHeight}px;
margin: 0;
}
`)
}
38 changes: 38 additions & 0 deletions packages/client/composables/useSlideScale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Ref } from 'vue'
import { computed } from 'vue'
import { useStyleTag } from '@vueuse/core'
import { slideAspect, slideHeight, slideWidth } from '../env'
import { slideScale } from '../state'
import { useNav } from './useNav'

export function useSlideScale(
containerSize: { width: Ref<number>, height: Ref<number> },
expectedWidth?: number,
isMain: boolean = false,
) {
const { isPrintMode } = useNav()

const width = computed(() => expectedWidth ?? containerSize.width.value)
const height = computed(() => expectedWidth ? expectedWidth / slideAspect.value : containerSize.height.value)

const scale = computed(() => {
if (slideScale.value && !isPrintMode.value)
return +slideScale.value
return Math.min(width.value / slideWidth.value, height.value / slideHeight.value)
})

const style = computed(() => ({
'height': `${slideHeight.value}px`,
'width': `${slideWidth.value}px`,
'transform': `translate(-50%, -50%) scale(${scale.value})`,
'--slidev-slide-scale': scale.value,
}))

if (isMain)
useStyleTag(computed(() => `:root { --slidev-slide-scale: ${scale.value}; }`))

return {
scale,
style,
}
}
2 changes: 2 additions & 0 deletions packages/client/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export const slideHeight = computed(() => Math.ceil(slideWidth.value / slideAspe
export const themeVars = computed(() => {
return objectMap(configs.themeConfig || {}, (k, v) => [`--slidev-theme-${k}`, v])
})

export const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
10 changes: 10 additions & 0 deletions packages/client/internals/Controls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
import { shallowRef } from 'vue'
import { showInfoDialog, showOverview, showRecordingDialog } from '../state'
import { configs } from '../env'
import { useNav } from '../composables/useNav'
import QuickOverview from './QuickOverview.vue'
import InfoDialog from './InfoDialog.vue'
import Goto from './Goto.vue'
import ContextMenu from './ContextMenu.vue'

const { isEmbedded } = useNav()
const drawingEnabled = __SLIDEV_FEATURE_DRAWINGS__ && !configs.drawings.presenterOnly && !isEmbedded.value
const DrawingControls = shallowRef<any>()
if (drawingEnabled)
import('../internals/DrawingControls.vue').then(v => DrawingControls.value = v.default)

const WebCamera = shallowRef<any>()
const RecordingDialog = shallowRef<any>()
if (__SLIDEV_FEATURE_RECORD__) {
Expand All @@ -16,6 +23,9 @@ if (__SLIDEV_FEATURE_RECORD__) {
</script>

<template>
<template v-if="drawingEnabled && DrawingControls">
<DrawingControls class="ml-0" />
</template>
<QuickOverview v-model="showOverview" />
<Goto />
<WebCamera v-if="WebCamera" />
Expand Down
2 changes: 1 addition & 1 deletion packages/client/internals/DrawingControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,4 @@ function setBrushColor(color: typeof brush.color) {
.v-popper--theme-menu .v-popper__arrow-inner {
--uno: border-main;
}
</style>../composables/drawings
</style>
2 changes: 1 addition & 1 deletion packages/client/internals/DrawingLayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ onBeforeUnmount(() => {
class="w-full h-full absolute top-0"
:class="{ 'pointer-events-none': !drawingEnabled, 'touch-none': drawingEnabled }"
/>
</template>../composables/drawings
</template>
2 changes: 1 addition & 1 deletion packages/client/internals/DrawingPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ const { drawingState } = useDrawings()
class="w-full h-full absolute top-0 pointer-events-none"
v-html="drawingState[page]"
/>
</template>../composables/drawings
</template>
Loading
Loading