@slidev/client 0.48.0-beta.10 → 0.48.0-beta.11
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/builtin/TocList.vue +3 -2
- package/composables/useClicks.ts +4 -1
- package/env.ts +3 -12
- package/internals/DrawingControls.vue +3 -5
- package/internals/Goto.vue +5 -4
- package/internals/NavControls.vue +1 -2
- package/internals/NoteEditor.vue +26 -2
- package/internals/PrintSlideClick.vue +2 -5
- package/internals/RecordingDialog.vue +3 -3
- package/internals/SlideContainer.vue +5 -3
- package/internals/SlidesShow.vue +7 -8
- package/layouts/two-cols-header.vue +9 -3
- package/logic/nav.ts +5 -0
- package/main.ts +3 -1
- package/package.json +3 -3
- package/pages/overview.vue +48 -4
- package/pages/presenter.vue +48 -46
- package/routes.ts +6 -36
- package/shim-vue.d.ts +35 -0
- package/shim.d.ts +0 -12
- package/styles/code.css +7 -3
- package/styles/index.css +13 -6
- package/styles/katex.css +1 -1
- package/styles/layouts-base.css +11 -8
- package/styles/vars.css +1 -1
- package/uno.config.ts +8 -0
package/builtin/TocList.vue
CHANGED
|
@@ -10,7 +10,7 @@ Usage:
|
|
|
10
10
|
import { computed } from 'vue'
|
|
11
11
|
import { toArray } from '@antfu/utils'
|
|
12
12
|
import type { TocItem } from '@slidev/types'
|
|
13
|
-
import Titles from '
|
|
13
|
+
import Titles from '#slidev/titles.md'
|
|
14
14
|
|
|
15
15
|
const props = withDefaults(defineProps<{
|
|
16
16
|
level: number
|
|
@@ -65,7 +65,8 @@ const styles = computed(() => {
|
|
|
65
65
|
.slidev-layout .slidev-toc-item p {
|
|
66
66
|
margin: 0;
|
|
67
67
|
}
|
|
68
|
-
.slidev-layout .slidev-toc-item div,
|
|
68
|
+
.slidev-layout .slidev-toc-item div,
|
|
69
|
+
.slidev-layout .slidev-toc-item div p {
|
|
69
70
|
display: initial;
|
|
70
71
|
}
|
|
71
72
|
</style>
|
package/composables/useClicks.ts
CHANGED
|
@@ -6,7 +6,10 @@ import type { RouteRecordRaw } from 'vue-router'
|
|
|
6
6
|
import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
|
|
7
7
|
import { normalizeAtProp } from '../logic/utils'
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export function useClicksContextBase(getCurrent: () => number, clicksOverrides?: number): ClicksContext {
|
|
10
13
|
const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
|
|
11
14
|
const map: ClicksContext['map'] = shallowReactive(new Map())
|
|
12
15
|
|
package/env.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import type { SlidevConfig } from '@slidev/types'
|
|
2
|
-
import type { UnwrapNestedRefs } from 'vue'
|
|
3
1
|
import { computed } from 'vue'
|
|
4
2
|
import { objectMap } from '@antfu/utils'
|
|
3
|
+
import configs from '#slidev/configs'
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
import _configs from '/@slidev/configs'
|
|
8
|
-
import type { SlidevContext } from './modules/context'
|
|
5
|
+
export { configs }
|
|
9
6
|
|
|
10
|
-
export const configs = _configs as SlidevConfig
|
|
11
7
|
export const slideAspect = configs.aspectRatio ?? (16 / 9)
|
|
12
8
|
export const slideWidth = configs.canvasWidth ?? 980
|
|
9
|
+
|
|
13
10
|
// To honor the aspect ratio more as possible, we need to approximate the height to the next integer.
|
|
14
11
|
// Doing this, we will prevent on print, to create an additional empty white page after each page.
|
|
15
12
|
export const slideHeight = Math.ceil(slideWidth / slideAspect)
|
|
@@ -17,9 +14,3 @@ export const slideHeight = Math.ceil(slideWidth / slideAspect)
|
|
|
17
14
|
export const themeVars = computed(() => {
|
|
18
15
|
return objectMap(configs.themeConfig || {}, (k, v) => [`--slidev-theme-${k}`, v])
|
|
19
16
|
})
|
|
20
|
-
|
|
21
|
-
declare module 'vue' {
|
|
22
|
-
interface ComponentCustomProperties {
|
|
23
|
-
$slidev: UnwrapNestedRefs<SlidevContext>
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -131,10 +131,8 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
131
131
|
</Draggable>
|
|
132
132
|
</template>
|
|
133
133
|
|
|
134
|
-
<style
|
|
135
|
-
.v-popper--theme-menu {
|
|
136
|
-
|
|
137
|
-
@apply border-main;
|
|
138
|
-
}
|
|
134
|
+
<style>
|
|
135
|
+
.v-popper--theme-menu .v-popper__arrow-inner {
|
|
136
|
+
--uno: border-main;
|
|
139
137
|
}
|
|
140
138
|
</style>
|
package/internals/Goto.vue
CHANGED
|
@@ -3,7 +3,7 @@ import { computed, ref, watch } from 'vue'
|
|
|
3
3
|
import Fuse from 'fuse.js'
|
|
4
4
|
import { go, rawRoutes } from '../logic/nav'
|
|
5
5
|
import { activeElement, showGotoDialog } from '../state'
|
|
6
|
-
import Titles from '
|
|
6
|
+
import Titles from '#slidev/titles.md'
|
|
7
7
|
|
|
8
8
|
const container = ref<HTMLDivElement>()
|
|
9
9
|
const input = ref<HTMLInputElement>()
|
|
@@ -165,10 +165,11 @@ watch(activeElement, () => {
|
|
|
165
165
|
</div>
|
|
166
166
|
</template>
|
|
167
167
|
|
|
168
|
-
<style scoped
|
|
168
|
+
<style scoped>
|
|
169
169
|
.autocomplete-list {
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
--uno: bg-main mt-1;
|
|
171
|
+
overflow: auto;
|
|
172
|
+
max-height: calc(100vh - 100px);
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
.autocomplete {
|
|
@@ -10,8 +10,7 @@ import MenuButton from './MenuButton.vue'
|
|
|
10
10
|
import VerticalDivider from './VerticalDivider.vue'
|
|
11
11
|
import IconButton from './IconButton.vue'
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
import CustomNavControls from '/@slidev/custom-nav-controls'
|
|
13
|
+
import CustomNavControls from '#slidev/custom-nav-controls'
|
|
15
14
|
|
|
16
15
|
const props = defineProps({
|
|
17
16
|
persist: {
|
package/internals/NoteEditor.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { nextTick, ref, watch, watchEffect } from 'vue'
|
|
2
3
|
import { ignorableWatch, onClickOutside, useVModel } from '@vueuse/core'
|
|
3
|
-
import { ref, watch, watchEffect } from 'vue'
|
|
4
4
|
import { useDynamicSlideInfo } from '../logic/note'
|
|
5
5
|
import NoteDisplay from './NoteDisplay.vue'
|
|
6
6
|
|
|
@@ -20,6 +20,9 @@ const props = defineProps({
|
|
|
20
20
|
placeholder: {
|
|
21
21
|
default: 'No notes for this slide',
|
|
22
22
|
},
|
|
23
|
+
autoHeight: {
|
|
24
|
+
default: false,
|
|
25
|
+
},
|
|
23
26
|
})
|
|
24
27
|
|
|
25
28
|
const emit = defineEmits([
|
|
@@ -66,6 +69,27 @@ watchEffect(() => {
|
|
|
66
69
|
onClickOutside(input, () => {
|
|
67
70
|
editing.value = false
|
|
68
71
|
})
|
|
72
|
+
|
|
73
|
+
function calculateHeight() {
|
|
74
|
+
if (!props.autoHeight || !input.value || !editing.value)
|
|
75
|
+
return
|
|
76
|
+
if (input.value.scrollHeight > input.value.clientHeight)
|
|
77
|
+
input.value.style.height = `${input.value.scrollHeight}px`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const inputHeight = ref<number | null>()
|
|
81
|
+
watchEffect(() => {
|
|
82
|
+
calculateHeight()
|
|
83
|
+
})
|
|
84
|
+
watch(
|
|
85
|
+
note,
|
|
86
|
+
() => {
|
|
87
|
+
nextTick(() => {
|
|
88
|
+
calculateHeight()
|
|
89
|
+
})
|
|
90
|
+
},
|
|
91
|
+
{ flush: 'post' },
|
|
92
|
+
)
|
|
69
93
|
</script>
|
|
70
94
|
|
|
71
95
|
<template>
|
|
@@ -83,7 +107,7 @@ onClickOutside(input, () => {
|
|
|
83
107
|
v-model="note"
|
|
84
108
|
class="prose resize-none overflow-auto outline-none bg-transparent block border-primary border-2"
|
|
85
109
|
style="line-height: 1.75;"
|
|
86
|
-
:style="props.style"
|
|
110
|
+
:style="[props.style, inputHeight != null ? { height: `${inputHeight}px` } : {}]"
|
|
87
111
|
:class="props.class"
|
|
88
112
|
:placeholder="placeholder"
|
|
89
113
|
@keydown.esc=" editing = false"
|
|
@@ -9,11 +9,8 @@ import { getSlideClass } from '../utils'
|
|
|
9
9
|
import type { SlidevContextNav } from '../modules/context'
|
|
10
10
|
import SlideWrapper from './SlideWrapper'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
// @ts-expect-error virtual module
|
|
16
|
-
import GlobalBottom from '/@slidev/global-components/bottom'
|
|
12
|
+
import GlobalTop from '#slidev/global-components/top'
|
|
13
|
+
import GlobalBottom from '#slidev/global-components/bottom'
|
|
17
14
|
|
|
18
15
|
const props = defineProps<{
|
|
19
16
|
clicksContext: ClicksContext
|
|
@@ -108,18 +108,18 @@ async function start() {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
input[type=
|
|
111
|
+
input[type='text'] {
|
|
112
112
|
@apply border border-main rounded px-2 py-1;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
button {
|
|
116
116
|
@apply bg-orange-400 text-white px-4 py-1 rounded border-b-2 border-orange-600;
|
|
117
|
-
@apply hover:(bg-orange-500 border-orange-700)
|
|
117
|
+
@apply hover:(bg-orange-500 border-orange-700);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
button.cancel {
|
|
121
121
|
@apply bg-gray-400 bg-opacity-50 text-white px-4 py-1 rounded border-b-2 border-main;
|
|
122
|
-
@apply hover:(bg-opacity-75 border-opacity-75)
|
|
122
|
+
@apply hover:(bg-opacity-75 border-opacity-75);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
</style>
|
|
@@ -3,7 +3,7 @@ import { provideLocal, useElementSize, useStyleTag } from '@vueuse/core'
|
|
|
3
3
|
import { computed, ref, watchEffect } from 'vue'
|
|
4
4
|
import { configs, slideAspect, slideHeight, slideWidth } from '../env'
|
|
5
5
|
import { injectionSlideScale } from '../constants'
|
|
6
|
-
import { isPrintMode } from '../logic/nav'
|
|
6
|
+
import { clicksDirection, isPrintMode } from '../logic/nav'
|
|
7
7
|
|
|
8
8
|
const props = defineProps({
|
|
9
9
|
width: {
|
|
@@ -55,6 +55,8 @@ const style = computed(() => ({
|
|
|
55
55
|
|
|
56
56
|
const className = computed(() => ({
|
|
57
57
|
'select-none': !configs.selectable,
|
|
58
|
+
'slidev-nav-go-forward': clicksDirection.value > 0,
|
|
59
|
+
'slidev-nav-go-backward': clicksDirection.value < 0,
|
|
58
60
|
}))
|
|
59
61
|
|
|
60
62
|
if (props.isMain) {
|
|
@@ -69,8 +71,8 @@ provideLocal(injectionSlideScale, scale as any)
|
|
|
69
71
|
</script>
|
|
70
72
|
|
|
71
73
|
<template>
|
|
72
|
-
<div id="slide-container" ref="root" :class="className">
|
|
73
|
-
<div id="slide-content" :style="style">
|
|
74
|
+
<div id="slide-container" ref="root" class="slidev-slides-container" :class="className">
|
|
75
|
+
<div id="slide-content" class="slidev-slide-content" :style="style">
|
|
74
76
|
<slot />
|
|
75
77
|
</div>
|
|
76
78
|
<slot name="controls" />
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -6,14 +6,11 @@ import { useViewTransition } from '../composables/useViewTransition'
|
|
|
6
6
|
import { skipTransition } from '../composables/hmr'
|
|
7
7
|
import { usePrimaryClicks } from '../composables/useClicks'
|
|
8
8
|
import SlideWrapper from './SlideWrapper'
|
|
9
|
-
|
|
10
|
-
// @ts-expect-error virtual module
|
|
11
|
-
import GlobalTop from '/@slidev/global-components/top'
|
|
12
|
-
|
|
13
|
-
// @ts-expect-error virtual module
|
|
14
|
-
import GlobalBottom from '/@slidev/global-components/bottom'
|
|
15
9
|
import PresenterMouse from './PresenterMouse.vue'
|
|
16
10
|
|
|
11
|
+
import GlobalTop from '#slidev/global-components/top'
|
|
12
|
+
import GlobalBottom from '#slidev/global-components/bottom'
|
|
13
|
+
|
|
17
14
|
defineProps<{
|
|
18
15
|
renderContext: 'slide' | 'presenter'
|
|
19
16
|
}>()
|
|
@@ -77,10 +74,12 @@ function onAfterLeave() {
|
|
|
77
74
|
|
|
78
75
|
<style scoped>
|
|
79
76
|
#slideshow {
|
|
80
|
-
|
|
77
|
+
height: 100%;
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
#slideshow > div {
|
|
84
|
-
|
|
81
|
+
position: absolute;
|
|
82
|
+
height: 100%;
|
|
83
|
+
width: 100%;
|
|
85
84
|
}
|
|
86
85
|
</style>
|
|
@@ -51,9 +51,15 @@ const props = defineProps({
|
|
|
51
51
|
grid-template-rows: repeat(2, 1fr);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
.col-header {
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
.col-header {
|
|
55
|
+
grid-area: 1 / 1 / 2 / 3;
|
|
56
|
+
}
|
|
57
|
+
.col-left {
|
|
58
|
+
grid-area: 2 / 1 / 3 / 2;
|
|
59
|
+
}
|
|
60
|
+
.col-right {
|
|
61
|
+
grid-area: 2 / 2 / 3 / 3;
|
|
62
|
+
}
|
|
57
63
|
.col-bottom {
|
|
58
64
|
align-self: end;
|
|
59
65
|
grid-area: 3 / 1 / 3 / 3;
|
package/logic/nav.ts
CHANGED
|
@@ -22,6 +22,7 @@ nextTick(() => {
|
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
export const navDirection = ref(0)
|
|
25
|
+
export const clicksDirection = ref(0)
|
|
25
26
|
|
|
26
27
|
export const route = computed(() => router.currentRoute.value)
|
|
27
28
|
|
|
@@ -84,6 +85,7 @@ watch(currentRoute, (next, prev) => {
|
|
|
84
85
|
})
|
|
85
86
|
|
|
86
87
|
export async function next() {
|
|
88
|
+
clicksDirection.value = 1
|
|
87
89
|
if (clicksTotal.value <= queryClicks.value)
|
|
88
90
|
await nextSlide()
|
|
89
91
|
else
|
|
@@ -91,6 +93,7 @@ export async function next() {
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
export async function prev() {
|
|
96
|
+
clicksDirection.value = -1
|
|
94
97
|
if (queryClicks.value <= 0)
|
|
95
98
|
await prevSlide()
|
|
96
99
|
else
|
|
@@ -102,11 +105,13 @@ export function getPath(no: number | string) {
|
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
export async function nextSlide() {
|
|
108
|
+
clicksDirection.value = 1
|
|
105
109
|
if (currentPage.value < rawRoutes.length)
|
|
106
110
|
await go(currentPage.value + 1)
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
export async function prevSlide(lastClicks = true) {
|
|
114
|
+
clicksDirection.value = -1
|
|
110
115
|
const next = Math.max(1, currentPage.value - 1)
|
|
111
116
|
await go(next)
|
|
112
117
|
if (lastClicks && clicksTotal.value)
|
package/main.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/// <reference types="@slidev/types/client" />
|
|
2
|
+
|
|
1
3
|
import { createApp } from 'vue'
|
|
2
4
|
import { createHead } from '@unhead/vue'
|
|
3
5
|
import App from './App.vue'
|
|
@@ -7,7 +9,7 @@ import { createVClickDirectives } from './modules/v-click'
|
|
|
7
9
|
import { createVMarkDirective } from './modules/v-mark'
|
|
8
10
|
import { createSlidevContext } from './modules/context'
|
|
9
11
|
|
|
10
|
-
import '
|
|
12
|
+
import '#slidev/styles'
|
|
11
13
|
|
|
12
14
|
const app = createApp(App)
|
|
13
15
|
app.use(router)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.48.0-beta.
|
|
4
|
+
"version": "0.48.0-beta.11",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"unocss": "^0.58.5",
|
|
55
55
|
"vue": "^3.4.19",
|
|
56
56
|
"vue-router": "^4.3.0",
|
|
57
|
-
"@slidev/
|
|
58
|
-
"@slidev/
|
|
57
|
+
"@slidev/parser": "0.48.0-beta.11",
|
|
58
|
+
"@slidev/types": "0.48.0-beta.11"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"vite": "^5.1.4"
|
package/pages/overview.vue
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { nextTick, onMounted, reactive, ref } from 'vue'
|
|
2
|
+
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
|
3
3
|
import { useHead } from '@unhead/vue'
|
|
4
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
5
|
+
import type { ClicksContext } from 'packages/types'
|
|
4
6
|
import { themeVars } from '../env'
|
|
5
7
|
import { rawRoutes } from '../logic/nav'
|
|
6
|
-
import {
|
|
8
|
+
import { useClicksContextBase } from '../composables/useClicks'
|
|
7
9
|
import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
|
|
8
10
|
import { getSlideClass } from '../utils'
|
|
9
11
|
import SlideContainer from '../internals/SlideContainer.vue'
|
|
@@ -21,6 +23,25 @@ useHead({
|
|
|
21
23
|
const blocks: Map<number, HTMLElement> = reactive(new Map())
|
|
22
24
|
const activeBlocks = ref<number[]>([])
|
|
23
25
|
const edittingNote = ref<number | null>(null)
|
|
26
|
+
const wordCounts = computed(() => rawRoutes.map(route => wordCount(route.meta?.slide?.note || '')))
|
|
27
|
+
const totalWords = computed(() => wordCounts.value.reduce((a, b) => a + b, 0))
|
|
28
|
+
const totalClicks = computed(() => rawRoutes.map(route => getSlideClicks(route)).reduce((a, b) => a + b, 0))
|
|
29
|
+
|
|
30
|
+
const clicksContextMap = new WeakMap<RouteRecordRaw, ClicksContext>()
|
|
31
|
+
function getClickContext(route: RouteRecordRaw) {
|
|
32
|
+
// We create a local clicks context to calculate the total clicks of the slide
|
|
33
|
+
if (!clicksContextMap.has(route))
|
|
34
|
+
clicksContextMap.set(route, useClicksContextBase(() => 999999, route?.meta?.clicks))
|
|
35
|
+
return clicksContextMap.get(route)!
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getSlideClicks(route: RouteRecordRaw) {
|
|
39
|
+
return route.meta?.clicks || getClickContext(route)?.total
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function wordCount(str: string) {
|
|
43
|
+
return str.match(/[\w\d\’\'-]+/gi)?.length || 0
|
|
44
|
+
}
|
|
24
45
|
|
|
25
46
|
function isElementInViewport(el: HTMLElement) {
|
|
26
47
|
const rect = el.getBoundingClientRect()
|
|
@@ -109,9 +130,17 @@ onMounted(() => {
|
|
|
109
130
|
class="relative border-t border-main of-hidden flex gap-4 min-h-50 group"
|
|
110
131
|
>
|
|
111
132
|
<div class="select-none w-13 text-right my4">
|
|
112
|
-
<div class="text-3xl op20">
|
|
133
|
+
<div class="text-3xl op20 mb2">
|
|
113
134
|
{{ idx + 1 }}
|
|
114
135
|
</div>
|
|
136
|
+
<div
|
|
137
|
+
v-if="getSlideClicks(route)"
|
|
138
|
+
class="flex gap-0.5 op50 items-center justify-end"
|
|
139
|
+
:title="`Clicks in this slide: ${getSlideClicks(route)}`"
|
|
140
|
+
>
|
|
141
|
+
<carbon:cursor-1 text-sm />
|
|
142
|
+
{{ getSlideClicks(route) }}
|
|
143
|
+
</div>
|
|
115
144
|
</div>
|
|
116
145
|
<div
|
|
117
146
|
class="border rounded border-main overflow-hidden bg-main my5 select-none h-max"
|
|
@@ -127,7 +156,7 @@ onMounted(() => {
|
|
|
127
156
|
<SlideWrapper
|
|
128
157
|
:is="route.component"
|
|
129
158
|
v-if="route?.component"
|
|
130
|
-
:clicks-context="
|
|
159
|
+
:clicks-context="getClickContext(route)"
|
|
131
160
|
:class="getSlideClass(route)"
|
|
132
161
|
:route="route"
|
|
133
162
|
render-context="overview"
|
|
@@ -148,10 +177,25 @@ onMounted(() => {
|
|
|
148
177
|
<NoteEditor
|
|
149
178
|
:no="idx"
|
|
150
179
|
class="max-w-250 w-250 text-lg rounded p3"
|
|
180
|
+
:auto-height="true"
|
|
151
181
|
:editing="edittingNote === idx"
|
|
182
|
+
@dblclick="edittingNote !== idx ? edittingNote = idx : null"
|
|
152
183
|
@update:editing="edittingNote = null"
|
|
153
184
|
/>
|
|
185
|
+
<div
|
|
186
|
+
v-if="wordCounts[idx] > 0"
|
|
187
|
+
class="select-none absolute bottom-0 right-0 bg-main rounded-tl p2 op35 text-xs"
|
|
188
|
+
>
|
|
189
|
+
{{ wordCounts[idx] }} words
|
|
190
|
+
</div>
|
|
154
191
|
</div>
|
|
155
192
|
</main>
|
|
193
|
+
<div class="absolute top-0 right-0 px3 py1.5 border-b border-l rounded-lb bg-main border-main select-none">
|
|
194
|
+
<div class="text-xs op50">
|
|
195
|
+
{{ rawRoutes.length }} slides ·
|
|
196
|
+
{{ totalClicks + rawRoutes.length - 1 }} clicks ·
|
|
197
|
+
{{ totalWords }} words
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
156
200
|
</div>
|
|
157
201
|
</template>
|
package/pages/presenter.vue
CHANGED
|
@@ -185,26 +185,26 @@ onMounted(() => {
|
|
|
185
185
|
<SlidesOverview v-model="showOverview" />
|
|
186
186
|
</template>
|
|
187
187
|
|
|
188
|
-
<style
|
|
188
|
+
<style scoped>
|
|
189
189
|
.slidev-presenter {
|
|
190
190
|
--slidev-controls-foreground: current;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
.timer-btn:hover {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
@apply opacity-100;
|
|
199
|
-
}
|
|
193
|
+
.timer-btn:hover > :first-child {
|
|
194
|
+
opacity: 0;
|
|
195
|
+
}
|
|
196
|
+
.timer-btn:hover > :last-child {
|
|
197
|
+
opacity: 1;
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
.section-title {
|
|
203
|
-
|
|
201
|
+
--uno: px-4 py-2 text-xl;
|
|
204
202
|
}
|
|
205
203
|
|
|
206
204
|
.grid-container {
|
|
207
|
-
|
|
205
|
+
--uno: bg-active;
|
|
206
|
+
height: 100%;
|
|
207
|
+
width: 100%;
|
|
208
208
|
display: grid;
|
|
209
209
|
gap: 1px 1px;
|
|
210
210
|
}
|
|
@@ -213,20 +213,20 @@ onMounted(() => {
|
|
|
213
213
|
grid-template-columns: 1fr 1fr;
|
|
214
214
|
grid-template-rows: min-content 2fr 1fr min-content;
|
|
215
215
|
grid-template-areas:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
'top top'
|
|
217
|
+
'main main'
|
|
218
|
+
'note next'
|
|
219
|
+
'bottom bottom';
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
.grid-container.layout2 {
|
|
223
223
|
grid-template-columns: 3fr 2fr;
|
|
224
224
|
grid-template-rows: min-content 2fr 1fr min-content;
|
|
225
225
|
grid-template-areas:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
'top top'
|
|
227
|
+
'note main'
|
|
228
|
+
'note next'
|
|
229
|
+
'bottom bottom';
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
@media (max-aspect-ratio: 3/5) {
|
|
@@ -234,11 +234,11 @@ onMounted(() => {
|
|
|
234
234
|
grid-template-columns: 1fr;
|
|
235
235
|
grid-template-rows: min-content 1fr 1fr 1fr min-content;
|
|
236
236
|
grid-template-areas:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
'top'
|
|
238
|
+
'main'
|
|
239
|
+
'note'
|
|
240
|
+
'next'
|
|
241
|
+
'bottom';
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
@@ -247,38 +247,40 @@ onMounted(() => {
|
|
|
247
247
|
grid-template-columns: 1fr 1.1fr 0.9fr;
|
|
248
248
|
grid-template-rows: min-content 1fr 2fr min-content;
|
|
249
249
|
grid-template-areas:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
250
|
+
'top top top'
|
|
251
|
+
'main main next'
|
|
252
|
+
'main main note'
|
|
253
|
+
'bottom bottom bottom';
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
.progress-bar {
|
|
258
|
-
|
|
258
|
+
--uno: fixed left-0 right-0 bottom-0;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
.grid-section {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
&.top {
|
|
265
|
-
grid-area: top;
|
|
266
|
-
}
|
|
267
|
-
&.main {
|
|
268
|
-
grid-area: main;
|
|
269
|
-
}
|
|
270
|
-
&.next {
|
|
271
|
-
grid-area: next;
|
|
272
|
-
}
|
|
273
|
-
&.note {
|
|
274
|
-
grid-area: note;
|
|
275
|
-
}
|
|
276
|
-
&.bottom {
|
|
277
|
-
grid-area: bottom;
|
|
278
|
-
}
|
|
262
|
+
--uno: bg-main;
|
|
279
263
|
}
|
|
280
264
|
|
|
265
|
+
.grid-section.top {
|
|
266
|
+
grid-area: top;
|
|
267
|
+
}
|
|
268
|
+
.grid-section.main {
|
|
269
|
+
grid-area: main;
|
|
270
|
+
}
|
|
271
|
+
.grid-section.next {
|
|
272
|
+
grid-area: next;
|
|
273
|
+
}
|
|
274
|
+
.grid-section.note {
|
|
275
|
+
grid-area: note;
|
|
276
|
+
}
|
|
277
|
+
.grid-section.bottom {
|
|
278
|
+
grid-area: bottom;
|
|
279
|
+
}
|
|
281
280
|
.context {
|
|
282
|
-
|
|
281
|
+
position: absolute;
|
|
282
|
+
top: 0;
|
|
283
|
+
left: 0;
|
|
284
|
+
--uno: px-1 text-xs bg-gray-400 bg-opacity-50 opacity-75 rounded-br-md;
|
|
283
285
|
}
|
|
284
286
|
</style>
|
package/routes.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
|
2
2
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
|
3
|
-
import type { TransitionGroupProps } from 'vue'
|
|
4
|
-
import type { ClicksContext, SlideInfo } from '@slidev/types'
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
import
|
|
4
|
+
import { rawRoutes, redirects } from '#slidev/routes'
|
|
5
|
+
import configs from '#slidev/configs'
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
import _configs from '/@slidev/configs'
|
|
11
|
-
|
|
12
|
-
export const rawRoutes = _rawRoutes as RouteRecordRaw[]
|
|
7
|
+
export { rawRoutes }
|
|
13
8
|
|
|
14
9
|
export const routes: RouteRecordRaw[] = [
|
|
15
10
|
{
|
|
@@ -34,12 +29,12 @@ export const routes: RouteRecordRaw[] = [
|
|
|
34
29
|
|
|
35
30
|
if (__SLIDEV_FEATURE_PRESENTER__) {
|
|
36
31
|
function passwordGuard(to: RouteLocationNormalized) {
|
|
37
|
-
if (!
|
|
32
|
+
if (!configs.remote || configs.remote === to.query.password)
|
|
38
33
|
return true
|
|
39
|
-
if (
|
|
34
|
+
if (configs.remote && to.query.password === undefined) {
|
|
40
35
|
// eslint-disable-next-line no-alert
|
|
41
36
|
const password = prompt('Enter password')
|
|
42
|
-
if (
|
|
37
|
+
if (configs.remote === password)
|
|
43
38
|
return true
|
|
44
39
|
}
|
|
45
40
|
if (to.params.no)
|
|
@@ -87,28 +82,3 @@ export const router = createRouter({
|
|
|
87
82
|
: createWebHistory(import.meta.env.BASE_URL),
|
|
88
83
|
routes,
|
|
89
84
|
})
|
|
90
|
-
|
|
91
|
-
declare module 'vue-router' {
|
|
92
|
-
interface RouteMeta {
|
|
93
|
-
// inherited from frontmatter
|
|
94
|
-
layout: string
|
|
95
|
-
name?: string
|
|
96
|
-
class?: string
|
|
97
|
-
clicks?: number
|
|
98
|
-
transition?: string | TransitionGroupProps | undefined
|
|
99
|
-
preload?: boolean
|
|
100
|
-
|
|
101
|
-
// slide info
|
|
102
|
-
slide?: Omit<SlideInfo, 'source'> & {
|
|
103
|
-
noteHTML: string
|
|
104
|
-
filepath: string
|
|
105
|
-
start: number
|
|
106
|
-
id: number
|
|
107
|
-
no: number
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// private fields
|
|
111
|
-
__clicksContext: null | ClicksContext
|
|
112
|
-
__preloaded?: boolean
|
|
113
|
-
}
|
|
114
|
-
}
|
package/shim-vue.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
declare module 'vue' {
|
|
2
|
+
import type { UnwrapNestedRefs } from 'vue'
|
|
3
|
+
import type { SlidevContext } from './modules/context'
|
|
4
|
+
|
|
5
|
+
interface ComponentCustomProperties {
|
|
6
|
+
$slidev: UnwrapNestedRefs<SlidevContext>
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare module 'vue-router' {
|
|
11
|
+
interface RouteMeta {
|
|
12
|
+
// inherited from frontmatter
|
|
13
|
+
layout: string
|
|
14
|
+
name?: string
|
|
15
|
+
class?: string
|
|
16
|
+
clicks?: number
|
|
17
|
+
transition?: string | TransitionGroupProps | undefined
|
|
18
|
+
preload?: boolean
|
|
19
|
+
|
|
20
|
+
// slide info
|
|
21
|
+
slide?: Omit<SlideInfo, 'source'> & {
|
|
22
|
+
noteHTML: string
|
|
23
|
+
filepath: string
|
|
24
|
+
start: number
|
|
25
|
+
id: number
|
|
26
|
+
no: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// private fields
|
|
30
|
+
__clicksContext: null | ClicksContext
|
|
31
|
+
__preloaded?: boolean
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {}
|
package/shim.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
declare interface Window {
|
|
2
|
-
// extend the window
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
declare module '*.vue';
|
|
6
|
-
|
|
7
1
|
// with unplugin-vue-markdown, markdowns can be treat as Vue components
|
|
8
2
|
declare module '*.md' {
|
|
9
3
|
import type { ComponentOptions } from 'vue'
|
|
@@ -12,12 +6,6 @@ declare module '*.md' {
|
|
|
12
6
|
export default component
|
|
13
7
|
}
|
|
14
8
|
|
|
15
|
-
declare module '/@slidev/configs' {
|
|
16
|
-
import { SlidevConfig } from '@slidev/types'
|
|
17
|
-
|
|
18
|
-
export default SlidevConfig
|
|
19
|
-
}
|
|
20
|
-
|
|
21
9
|
declare module 'mermaid/dist/mermaid.esm.mjs' {
|
|
22
10
|
import Mermaid from 'mermaid/dist/mermaid.d.ts'
|
|
23
11
|
|
package/styles/code.css
CHANGED
|
@@ -59,7 +59,9 @@ html:not(.dark) .shiki span {
|
|
|
59
59
|
.slidev-code-line-numbers .slidev-code code .line::before {
|
|
60
60
|
content: counter(step);
|
|
61
61
|
counter-increment: step;
|
|
62
|
-
|
|
62
|
+
display: inline-block;
|
|
63
|
+
text-align: right;
|
|
64
|
+
--uno: w-4 mr-6 text-gray-400 dark:text-gray-600;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/* Inline Code */
|
|
@@ -67,7 +69,7 @@ html:not(.dark) .shiki span {
|
|
|
67
69
|
font-size: 0.9em;
|
|
68
70
|
background: var(--slidev-code-background);
|
|
69
71
|
border-radius: var(--slidev-code-radius);
|
|
70
|
-
|
|
72
|
+
--uno: font-light py-0.5 px-1.5;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
.slidev-layout :not(pre) > code:before {
|
|
@@ -82,4 +84,6 @@ html:not(.dark) .shiki span {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/* CodeMirror */
|
|
85
|
-
.CodeMirror pre.CodeMirror-placeholder {
|
|
87
|
+
.CodeMirror pre.CodeMirror-placeholder {
|
|
88
|
+
opacity: 0.4;
|
|
89
|
+
}
|
package/styles/index.css
CHANGED
|
@@ -23,15 +23,16 @@ html {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.slidev-icon-btn.shallow {
|
|
26
|
-
|
|
26
|
+
opacity: 0.3;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.slidev-icon-btn.active {
|
|
30
|
-
|
|
30
|
+
opacity: 1;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
.slidev-icon-btn.disabled {
|
|
34
|
-
|
|
34
|
+
opacity: 0.25;
|
|
35
|
+
pointer-events: none;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
.slidev-vclick-target {
|
|
@@ -39,11 +40,13 @@ html {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
.slidev-vclick-hidden {
|
|
42
|
-
|
|
43
|
+
opacity: 0 !important;
|
|
44
|
+
pointer-events: none !important;
|
|
45
|
+
user-select: none !important;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
.slidev-vclick-fade {
|
|
46
|
-
|
|
49
|
+
opacity: 0.5;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
.slidev-icon {
|
|
@@ -53,7 +56,11 @@ html {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
.slidev-page {
|
|
56
|
-
|
|
59
|
+
position: relative;
|
|
60
|
+
top: 0;
|
|
61
|
+
left: 0;
|
|
62
|
+
right: 0;
|
|
63
|
+
width: 100%;
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
/* Transform the position back for Rough Notation (v-mark) */
|
package/styles/katex.css
CHANGED
package/styles/layouts-base.css
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
.slidev-layout {
|
|
2
2
|
@apply px-14 py-10 text-[1.1rem] h-full;
|
|
3
3
|
|
|
4
|
-
pre,
|
|
4
|
+
pre,
|
|
5
|
+
code {
|
|
5
6
|
@apply select-text;
|
|
6
7
|
}
|
|
7
8
|
|
|
@@ -79,11 +80,13 @@
|
|
|
79
80
|
@apply border-current border-b border-dashed hover:text-primary hover:border-solid;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
td,
|
|
83
|
+
td,
|
|
84
|
+
th {
|
|
83
85
|
@apply p-2 py-3;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
b,
|
|
88
|
+
b,
|
|
89
|
+
strong {
|
|
87
90
|
@apply font-600;
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -94,8 +97,8 @@
|
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
.slidev-layout,
|
|
97
|
-
[dir=ltr],
|
|
98
|
-
.slidev-layout [dir=ltr] {
|
|
100
|
+
[dir='ltr'],
|
|
101
|
+
.slidev-layout [dir='ltr'] {
|
|
99
102
|
h1 {
|
|
100
103
|
@apply -ml-[0.05em] mr-0;
|
|
101
104
|
}
|
|
@@ -109,10 +112,10 @@
|
|
|
109
112
|
}
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
[dir=rtl],
|
|
113
|
-
.slidev-layout [dir=rtl] {
|
|
115
|
+
[dir='rtl'],
|
|
116
|
+
.slidev-layout [dir='rtl'] {
|
|
114
117
|
h1 {
|
|
115
|
-
@apply -mr-[0.05em] ml-0;
|
|
118
|
+
@apply -mr-[0.05em] ml-0;
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
h6 {
|
package/styles/vars.css
CHANGED
package/uno.config.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
transformerDirectives,
|
|
9
9
|
transformerVariantGroup,
|
|
10
10
|
} from 'unocss'
|
|
11
|
+
import { variantMatcher } from '@unocss/preset-mini/utils'
|
|
11
12
|
|
|
12
13
|
export default defineConfig({
|
|
13
14
|
safelist: [
|
|
@@ -28,6 +29,13 @@ export default defineConfig({
|
|
|
28
29
|
'abs-bl': 'absolute bottom-0 left-0',
|
|
29
30
|
'abs-br': 'absolute bottom-0 right-0',
|
|
30
31
|
},
|
|
32
|
+
// Slidev Specific Variants, probably extrat to a preset later
|
|
33
|
+
variants: [
|
|
34
|
+
// `forward:` and `backward:` variant to selectively apply styles based on the direction of the slide
|
|
35
|
+
// For example, `forward:text-red` will only apply to the slides that are navigated forward
|
|
36
|
+
variantMatcher('forward', input => ({ prefix: `.slidev-nav-go-forward ${input.prefix}` })),
|
|
37
|
+
variantMatcher('backward', input => ({ prefix: `.slidev-nav-go-backward ${input.prefix}` })),
|
|
38
|
+
],
|
|
31
39
|
presets: [
|
|
32
40
|
presetUno(),
|
|
33
41
|
presetAttributify(),
|