@slidev/client 0.40.2 → 0.40.4

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/Link.vue CHANGED
@@ -10,8 +10,6 @@ Usage:
10
10
  <script setup lang="ts">
11
11
  import { isPrintMode } from '../logic/nav'
12
12
 
13
- /* eslint-disable vue/no-v-text-v-html-on-component */
14
-
15
13
  defineProps<{
16
14
  to: number | string
17
15
  title?: string
@@ -18,28 +18,30 @@ import { decode } from 'js-base64'
18
18
  import { nanoid } from 'nanoid'
19
19
  import { isDark } from '../logic/dark'
20
20
 
21
- const props = defineProps({
22
- code: {
23
- default: '',
24
- },
25
- lang: {
26
- default: 'typescript',
27
- },
28
- readonly: {
29
- default: false,
30
- },
31
- lineNumbers: {
32
- default: 'off',
33
- },
34
- height: {
35
- default: 'auto',
36
- },
21
+ const props = withDefaults(defineProps<{
22
+ code: string
23
+ diff?: string
24
+ lang?: string
25
+ readonly?: boolean
26
+ lineNumbers?: 'on' | 'off' | 'relative' | 'interval'
27
+ height?: number | string
28
+ }>(), {
29
+ code: '',
30
+ lang: 'typescript',
31
+ readonly: false,
32
+ lineNumbers: 'off',
33
+ height: 'auto',
37
34
  })
38
35
 
39
36
  const id = nanoid()
40
37
  const code = ref(decode(props.code).trimEnd())
38
+ const diff = ref(props.diff ? decode(props.diff).trimEnd() : null)
41
39
  const lineHeight = +(getComputedStyle(document.body).getPropertyValue('--slidev-code-line-height') || '18').replace('px', '') || 18
42
- const height = computed(() => props.height === 'auto' ? `${code.value.split(/\r?\n/g).length * lineHeight + 20}px` : props.height)
40
+ const editorHeight = ref(0)
41
+ const calculatedHeight = computed(() => code.value.split(/\r?\n/g).length * lineHeight)
42
+ const height = computed(() => {
43
+ return props.height === 'auto' ? `${Math.max(calculatedHeight.value, editorHeight.value) + 20}px` : props.height
44
+ })
43
45
 
44
46
  const iframe = ref<HTMLIFrameElement>()
45
47
 
@@ -73,9 +75,13 @@ onMounted(() => {
73
75
  'allow-top-navigation-by-user-activation',
74
76
  ].join(' '))
75
77
 
76
- frame.src = __DEV__
77
- ? `${location.origin}${__SLIDEV_CLIENT_ROOT__}/iframes/monaco/index.html`
78
- : `${import.meta.env.BASE_URL}iframes/monaco/index.html`
78
+ let src = __DEV__
79
+ ? `${location.origin}${__SLIDEV_CLIENT_ROOT__}/`
80
+ : import.meta.env.BASE_URL
81
+ src += `iframes/monaco/index.html?id=${id}&lineNumbers=${props.lineNumbers}&lang=${props.lang}`
82
+ if (diff.value)
83
+ src += '&diff=1'
84
+ frame.src = src
79
85
 
80
86
  frame.style.backgroundColor = 'transparent'
81
87
  })
@@ -85,18 +91,21 @@ function post(payload: any) {
85
91
  JSON.stringify({
86
92
  type: 'slidev-monaco',
87
93
  data: payload,
94
+ id,
88
95
  }),
89
96
  location.origin,
90
97
  )
91
98
  }
92
99
 
93
100
  useEventListener(window, 'message', ({ data: payload }) => {
101
+ if (payload.id !== id)
102
+ return
94
103
  if (payload.type === 'slidev-monaco-loaded') {
95
104
  if (iframe.value) {
96
105
  post({
97
106
  code: code.value,
107
+ diff: diff.value,
98
108
  lang: props.lang,
99
- id,
100
109
  readonly: props.readonly,
101
110
  lineNumbers: props.lineNumbers,
102
111
  dark: isDark.value,
@@ -105,11 +114,14 @@ useEventListener(window, 'message', ({ data: payload }) => {
105
114
  }
106
115
  return
107
116
  }
108
- if (payload.type !== 'slidev-monaco' || payload.id !== id)
109
- return
110
- if (!payload?.data?.code || code.value === payload.data.code)
117
+ if (payload.type !== 'slidev-monaco')
111
118
  return
112
- code.value = payload.data.code
119
+ if (payload.data?.height)
120
+ editorHeight.value = payload.data?.height
121
+ if (payload?.data?.code && code.value !== payload.data.code)
122
+ code.value = payload.data.code
123
+ if (payload?.data?.diff && diff.value !== payload.data.diff)
124
+ diff.value = payload.data.diff
113
125
  })
114
126
  </script>
115
127
 
@@ -6,10 +6,17 @@
6
6
 
7
7
  import { toArray } from '@antfu/utils'
8
8
  import type { Directive, VNode, VNodeArrayChildren } from 'vue'
9
+
9
10
  import { defineComponent, h, isVNode, resolveDirective, withDirectives } from 'vue'
10
11
 
12
+ const listTags = ['ul', 'ol']
13
+
11
14
  export default defineComponent({
12
15
  props: {
16
+ depth: {
17
+ type: [Number, String],
18
+ default: 1,
19
+ },
13
20
  every: {
14
21
  type: Number,
15
22
  default: 1,
@@ -31,17 +38,17 @@ export default defineComponent({
31
38
  const click = resolveDirective('click')!
32
39
  const after = resolveDirective('after')!
33
40
 
34
- const applyDirective = (node: VNode, directive: Directive, delta: number) =>
35
- withDirectives(node, [[
41
+ const applyDirective = (node: VNode, directive: Directive, delta: number | string) => {
42
+ return withDirectives(node, [[
36
43
  directive,
37
- this.at != null
38
- ? +this.at + delta
39
- : null, '',
44
+ delta,
45
+ '',
40
46
  {
41
47
  hide: this.hide,
42
48
  fade: this.fade,
43
49
  },
44
50
  ]])
51
+ }
45
52
 
46
53
  let defaults = this.$slots.default?.()
47
54
 
@@ -50,22 +57,58 @@ export default defineComponent({
50
57
 
51
58
  defaults = toArray(defaults)
52
59
 
53
- const mapChildren = (children: VNodeArrayChildren) => {
54
- return children.map((i, idx) =>
55
- isVNode(i)
56
- ? applyDirective(
57
- h(i),
58
- idx % this.every === 0 ? click : after,
59
- Math.floor(idx / this.every),
60
- )
61
- : i,
62
- )
60
+ const mapSubList = (children: VNodeArrayChildren, depth = 1): [VNodeArrayChildren, number] => {
61
+ let idx = 0
62
+ const vNodes = children.map((i) => {
63
+ if (!isVNode(i))
64
+ return i
65
+ if (listTags.includes(i.type as string) && Array.isArray(i.children)) {
66
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
67
+ const [vNodes, total] = mapChildren(i.children, depth + 1)
68
+ idx += total
69
+ return h(i, {}, [vNodes])
70
+ }
71
+ return h(i)
72
+ })
73
+ return [vNodes, idx]
74
+ }
75
+
76
+ let globalIdx = 0
77
+ const mapChildren = (children: VNodeArrayChildren, depth = 1): [VNodeArrayChildren, number] => {
78
+ let idx = 0
79
+ const vNodes = children.map((i) => {
80
+ if (!isVNode(i))
81
+ return i
82
+ const directive = idx % this.every === 0 ? click : after
83
+ let vNode
84
+ let childCount = 0
85
+ if (depth < this.depth && Array.isArray(i.children)) {
86
+ const [vNodes, total] = mapSubList(i.children, depth)
87
+ vNode = h(i, {}, [vNodes])
88
+ childCount = total
89
+ idx += total + 1
90
+ }
91
+ else {
92
+ vNode = h(i)
93
+ idx++
94
+ }
95
+ const delta = this.at != null
96
+ ? Number(this.at) + Math.floor(globalIdx / this.every) + depth
97
+ : (depth - 1 - childCount).toString()
98
+ globalIdx++
99
+ return applyDirective(
100
+ vNode,
101
+ directive,
102
+ (typeof delta === 'string' && !delta.startsWith('-')) ? `+${delta}` : delta,
103
+ )
104
+ })
105
+ return [vNodes, idx]
63
106
  }
64
107
 
65
108
  // handle ul, ol list
66
- if (defaults.length === 1 && ['ul', 'ol'].includes(defaults[0].type as string) && Array.isArray(defaults[0].children))
67
- return h(defaults[0], {}, [mapChildren(defaults[0].children)])
109
+ if (defaults.length === 1 && listTags.includes(defaults[0].type as string) && Array.isArray(defaults[0].children))
110
+ return h(defaults[0], {}, [mapChildren(defaults[0].children)[0]])
68
111
 
69
- return mapChildren(defaults)
112
+ return mapChildren(defaults)[0]
70
113
  },
71
114
  })
@@ -1,7 +1,7 @@
1
1
  import type { ComputedRef, WritableComputedRef } from 'vue'
2
2
  import { computed } from 'vue'
3
3
  import type { RouteLocationNormalizedLoaded } from 'vue-router'
4
- import { downloadPDF, next, nextSlide, openInEditor, prev, prevSlide } from '../logic/nav'
4
+ import type { SlidevContext } from '../modules/context'
5
5
  import { configs } from '../env'
6
6
  import { useNav } from './useNav'
7
7
  import { useNavClicks } from './useNavClicks'
@@ -9,19 +9,13 @@ import { useNavClicks } from './useNavClicks'
9
9
  export function useContext(
10
10
  route: ComputedRef<RouteLocationNormalizedLoaded>,
11
11
  clicks: WritableComputedRef<number>,
12
- ) {
12
+ ): SlidevContext {
13
13
  const nav = useNav(route)
14
14
  const navClicks = useNavClicks(clicks, nav.currentRoute, nav.currentPage)
15
15
  return {
16
16
  nav: {
17
17
  ...nav,
18
18
  ...navClicks,
19
- downloadPDF,
20
- next,
21
- nextSlide,
22
- openInEditor,
23
- prev,
24
- prevSlide,
25
19
  },
26
20
  configs,
27
21
  themeConfigs: computed(() => configs.themeConfig),
@@ -2,10 +2,11 @@ import type { ComputedRef } from 'vue'
2
2
  import { computed } from 'vue'
3
3
  import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
4
4
  import type { TocItem } from '@slidev/types'
5
- import { addToTree, filterTree, getPath, getTreeWithActiveStatuses } from '../logic/nav'
5
+ import type { SlidevContextNav } from '../modules/context'
6
+ import { addToTree, downloadPDF, filterTree, getPath, getTreeWithActiveStatuses, next, nextSlide, openInEditor, prev, prevSlide } from '../logic/nav'
6
7
  import { rawRoutes } from '../routes'
7
8
 
8
- export function useNav(route: ComputedRef<RouteRecordRaw | RouteLocationNormalizedLoaded>) {
9
+ export function useNav(route: ComputedRef<RouteRecordRaw | RouteLocationNormalizedLoaded>): SlidevContextNav {
9
10
  const path = computed(() => route.value.path)
10
11
  const total = computed(() => rawRoutes.length - 1)
11
12
 
@@ -39,5 +40,11 @@ export function useNav(route: ComputedRef<RouteRecordRaw | RouteLocationNormaliz
39
40
  rawTree,
40
41
  treeWithActiveStatuses,
41
42
  tree,
43
+ downloadPDF,
44
+ next,
45
+ nextSlide,
46
+ openInEditor,
47
+ prev,
48
+ prevSlide,
42
49
  }
43
50
  }
@@ -1,13 +1,14 @@
1
1
  import type { ComputedRef, WritableComputedRef } from 'vue'
2
2
  import { computed, nextTick, ref } from 'vue'
3
3
  import type { RouteRecordRaw } from 'vue-router'
4
+ import type { SlidevContextNavClicks } from '../modules/context'
4
5
  import { rawRoutes, router } from '../routes'
5
6
 
6
7
  export function useNavClicks(
7
8
  clicks: WritableComputedRef<number>,
8
9
  currentRoute: ComputedRef<RouteRecordRaw | undefined>,
9
10
  currentPage: ComputedRef<number>,
10
- ) {
11
+ ): SlidevContextNavClicks {
11
12
  // force update collected elements when the route is fully resolved
12
13
  const routeForceRefresh = ref(0)
13
14
  nextTick(() => {
package/constants.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ComputedRef, InjectionKey, Ref } from 'vue'
1
+ import type { ComputedRef, InjectionKey, Ref, UnwrapNestedRefs } from 'vue'
2
2
  import type { RouteRecordRaw } from 'vue-router'
3
3
  import type { RenderContext } from '@slidev/types'
4
4
  import type { SlidevContext } from './modules/context'
@@ -8,7 +8,7 @@ export const injectionClicksElements: InjectionKey<Ref<(Element | string)[]>> =
8
8
  export const injectionOrderMap: InjectionKey<Ref<Map<number, HTMLElement[]>>> = Symbol('v-click-clicks-order-map')
9
9
  export const injectionClicksDisabled: InjectionKey<Ref<boolean>> = Symbol('v-click-clicks-disabled')
10
10
  export const injectionSlideScale: InjectionKey<ComputedRef<number>> = Symbol('slidev-slide-scale')
11
- export const injectionSlidevContext: InjectionKey<SlidevContext> = Symbol('slidev-slidev-context')
11
+ export const injectionSlidevContext: InjectionKey<UnwrapNestedRefs<SlidevContext>> = Symbol('slidev-slidev-context')
12
12
  export const injectionRoute: InjectionKey<RouteRecordRaw> = Symbol('slidev-route')
13
13
  export const injectionSlideContext: InjectionKey<RenderContext> = Symbol('slidev-slide-context')
14
14
 
@@ -3,13 +3,12 @@ body,
3
3
  #container {
4
4
  padding: 0;
5
5
  margin: 0;
6
- background: transparent;
6
+ background: var(--slidev-code-background);
7
7
  width: 100%;
8
8
  height: 100%;
9
9
  }
10
10
 
11
11
  #container {
12
- background: var(--slidev-code-background);
13
12
  padding: var(--slidev-code-padding);
14
13
  margin: var(--slidev-code-margin);
15
14
  border-radius: var(--slidev-code-radius);
@@ -6,18 +6,22 @@ import { formatCode } from '../../setup/prettier'
6
6
  import setupMonaco from '../../setup/monaco'
7
7
  import '/@slidev/monaco-types'
8
8
 
9
+ const url = new URL(location.href)
9
10
  const props = {
10
- id: Math.random().toString(),
11
+ id: url.searchParams.get('id'),
11
12
  code: '',
12
- lang: 'typescript',
13
+ diff: '',
14
+ lang: url.searchParams.get('lang') ?? 'typescript',
13
15
  readonly: false,
14
- lineNumbers: 'off',
16
+ lineNumbers: url.searchParams.get('lineNumbers') ?? 'off',
15
17
  dark: false,
16
18
  style: '',
17
19
  }
18
20
 
19
21
  const styleObject = document.createElement('style')
20
- let editor: monaco.editor.IStandaloneCodeEditor
22
+ let originalEditor: monaco.editor.IStandaloneCodeEditor
23
+ let modifiedEditor: monaco.editor.IStandaloneCodeEditor
24
+ let format: () => void = () => { }
21
25
  let update: () => void = () => { }
22
26
 
23
27
  document.body.appendChild(styleObject)
@@ -44,92 +48,193 @@ const ext = () => {
44
48
  }
45
49
  }
46
50
 
51
+ function post(data: any, type = 'slidev-monaco') {
52
+ if (window.parent === window)
53
+ return
54
+
55
+ window.parent.postMessage(
56
+ {
57
+ type,
58
+ id: props.id,
59
+ data,
60
+ },
61
+ location.origin,
62
+ )
63
+ }
64
+
47
65
  async function start() {
48
66
  const { monaco, theme = {} } = await setupMonaco()
49
67
 
68
+ const style = getComputedStyle(document.documentElement)
69
+ const container = document.getElementById('container')!
70
+
50
71
  const model = monaco.editor.createModel(
51
72
  props.code,
52
73
  lang(),
53
74
  monaco.Uri.parse(`file:///root/${Date.now()}.${ext()}`),
54
75
  )
55
76
 
56
- const style = getComputedStyle(document.documentElement)
57
- const container = document.getElementById('container')!
77
+ if (url.searchParams.get('diff')) {
78
+ // Diff editor
79
+ const diffModel = monaco.editor.createModel(
80
+ props.diff,
81
+ lang(),
82
+ monaco.Uri.parse(`file:///root/${Date.now()}.${ext()}`),
83
+ )
84
+ const monacoEditor = monaco.editor.createDiffEditor(container, {
85
+ fontSize: +style.getPropertyValue('--slidev-code-font-size').replace(/px/g, ''),
86
+ fontFamily: style.getPropertyValue('--slidev-code-font-family'),
87
+ lineHeight: +style.getPropertyValue('--slidev-code-line-height').replace(/px/g, ''),
88
+ lineDecorationsWidth: 0,
89
+ lineNumbersMinChars: 0,
90
+ scrollBeyondLastLine: false,
91
+ scrollBeyondLastColumn: 0,
92
+ automaticLayout: true,
93
+ readOnly: props.readonly,
94
+ theme: 'vitesse-dark',
95
+ lineNumbers: props.lineNumbers as any,
96
+ glyphMargin: false,
97
+ scrollbar: {
98
+ useShadows: false,
99
+ vertical: 'hidden',
100
+ horizontal: 'hidden',
101
+ },
102
+ overviewRulerLanes: 0,
103
+ minimap: { enabled: false },
104
+ enableSplitViewResizing: false,
105
+ renderOverviewRuler: false,
106
+ // renderSideBySide: false,
107
+ })
108
+ monacoEditor.setModel({
109
+ original: model,
110
+ modified: diffModel,
111
+ })
112
+ originalEditor = monacoEditor.getOriginalEditor()
113
+ modifiedEditor = monacoEditor.getModifiedEditor()
114
+
115
+ format = async () => {
116
+ model.setValue((await formatCode(props.code, lang())).trim())
117
+ diffModel.setValue((await formatCode(props.diff, lang())).trim())
118
+ }
58
119
 
59
- editor = monaco.editor.create(container, {
60
- model,
61
- tabSize: 2,
62
- insertSpaces: true,
63
- detectIndentation: false,
64
- folding: false,
65
- fontSize: +style.getPropertyValue('--slidev-code-font-size').replace(/px/g, ''),
66
- fontFamily: style.getPropertyValue('--slidev-code-font-family'),
67
- lineHeight: +style.getPropertyValue('--slidev-code-line-height').replace(/px/g, ''),
68
- lineDecorationsWidth: 0,
69
- lineNumbersMinChars: 0,
70
- scrollBeyondLastLine: false,
71
- scrollBeyondLastColumn: 0,
72
- automaticLayout: true,
73
- readOnly: props.readonly,
74
- theme: 'vitesse-dark',
75
- lineNumbers: props.lineNumbers as any,
76
- glyphMargin: false,
77
- scrollbar: {
78
- useShadows: false,
79
- vertical: 'hidden',
80
- horizontal: 'hidden',
81
- },
82
- overviewRulerLanes: 0,
83
- minimap: { enabled: false },
84
- })
120
+ // ctrl+s to format
121
+ originalEditor.onKeyDown((e) => {
122
+ if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
123
+ e.preventDefault()
124
+ format()
125
+ }
126
+ })
127
+ modifiedEditor.onKeyDown((e) => {
128
+ if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
129
+ e.preventDefault()
130
+ format()
131
+ }
132
+ })
133
+
134
+ update = () => {
135
+ monaco.editor.setTheme(props.dark
136
+ ? (theme.dark || 'vitesse-dark')
137
+ : (theme.light || 'vitesse-light'),
138
+ )
139
+ styleObject.innerHTML = `:root { ${props.style} }`
140
+
141
+ if (originalEditor.getValue().toString() !== props.code) {
142
+ const selection = originalEditor.getSelection()
143
+ originalEditor.setValue(props.code)
144
+ if (selection)
145
+ originalEditor.setSelection(selection)
146
+ }
147
+
148
+ if (modifiedEditor.getValue().toString() !== props.diff) {
149
+ const selection = modifiedEditor.getSelection()
150
+ modifiedEditor.setValue(props.diff)
151
+ if (selection)
152
+ modifiedEditor.setSelection(selection)
153
+ }
154
+ }
85
155
 
86
- async function format() {
87
- model.setValue((await formatCode(props.code, lang())).trim())
88
- }
156
+ diffModel.onDidChangeContent(() => {
157
+ onCodeChange(diffModel.getValue().toString())
158
+ })
89
159
 
90
- model.onDidChangeContent(() => {
91
- onCodeChange(model.getValue().toString())
92
- })
93
-
94
- // ctrl+s to format
95
- editor.onKeyDown((e) => {
96
- if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
97
- e.preventDefault()
98
- format()
160
+ function onCodeChange(diff: string) {
161
+ props.diff = diff
162
+ post({ diff })
99
163
  }
100
- })
164
+ }
165
+ else {
166
+ // Normal editor
167
+ originalEditor = monaco.editor.create(container, {
168
+ model,
169
+ tabSize: 2,
170
+ insertSpaces: true,
171
+ detectIndentation: false,
172
+ folding: false,
173
+ fontSize: +style.getPropertyValue('--slidev-code-font-size').replace(/px/g, ''),
174
+ fontFamily: style.getPropertyValue('--slidev-code-font-family'),
175
+ lineHeight: +style.getPropertyValue('--slidev-code-line-height').replace(/px/g, ''),
176
+ lineDecorationsWidth: 0,
177
+ lineNumbersMinChars: 0,
178
+ scrollBeyondLastLine: false,
179
+ scrollBeyondLastColumn: 0,
180
+ automaticLayout: true,
181
+ readOnly: props.readonly,
182
+ theme: 'vitesse-dark',
183
+ lineNumbers: props.lineNumbers as any,
184
+ glyphMargin: false,
185
+ scrollbar: {
186
+ useShadows: false,
187
+ vertical: 'hidden',
188
+ horizontal: 'hidden',
189
+ },
190
+ overviewRulerLanes: 0,
191
+ minimap: { enabled: false },
192
+ })
101
193
 
102
- update = () => {
103
- monaco.editor.setTheme(props.dark
104
- ? (theme.dark || 'vitesse-dark')
105
- : (theme.light || 'vitesse-light'),
106
- )
107
- styleObject.innerHTML = `:root { ${props.style} }`
194
+ format = async () => {
195
+ model.setValue((await formatCode(props.code, lang())).trim())
196
+ }
108
197
 
109
- if (editor.getValue().toString() !== props.code) {
110
- const selection = editor.getSelection()
111
- editor.setValue(props.code)
112
- if (selection)
113
- editor.setSelection(selection)
198
+ // ctrl+s to format
199
+ originalEditor.onKeyDown((e) => {
200
+ if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
201
+ e.preventDefault()
202
+ format()
203
+ }
204
+ })
205
+
206
+ update = () => {
207
+ monaco.editor.setTheme(props.dark
208
+ ? (theme.dark || 'vitesse-dark')
209
+ : (theme.light || 'vitesse-light'),
210
+ )
211
+ styleObject.innerHTML = `:root { ${props.style} }`
212
+
213
+ if (originalEditor.getValue().toString() !== props.code) {
214
+ const selection = originalEditor.getSelection()
215
+ originalEditor.setValue(props.code)
216
+ if (selection)
217
+ originalEditor.setSelection(selection)
218
+ }
114
219
  }
115
220
  }
116
221
 
222
+ originalEditor.onDidContentSizeChange(() => {
223
+ post({ height: Math.max(originalEditor.getContentHeight(), modifiedEditor?.getContentHeight() ?? 0) })
224
+ })
225
+
226
+ model.onDidChangeContent(() => {
227
+ onCodeChange(model.getValue().toString())
228
+ })
229
+
117
230
  function onCodeChange(code: string) {
118
231
  props.code = code
119
- if (window.parent === window)
120
- return
121
-
122
- window.parent.postMessage(
123
- {
124
- type: 'slidev-monaco',
125
- id: props.id,
126
- data: { code },
127
- },
128
- location.origin,
129
- )
232
+ post({ code })
130
233
  }
131
234
 
132
235
  update()
236
+
237
+ post({}, 'slidev-monaco-loaded')
133
238
  }
134
239
 
135
240
  window.addEventListener('message', (payload) => {
@@ -139,13 +244,11 @@ window.addEventListener('message', (payload) => {
139
244
  return
140
245
  if (typeof payload.data !== 'string')
141
246
  return
142
- const { type, data } = JSON.parse(payload.data)
143
- if (type === 'slidev-monaco') {
247
+ const { type, data, id } = JSON.parse(payload.data)
248
+ if (type === 'slidev-monaco' && id === props.id) {
144
249
  Object.assign(props, data)
145
250
  update()
146
251
  }
147
252
  })
148
253
 
149
- window.parent.postMessage({ type: 'slidev-monaco-loaded' }, location.origin)
150
-
151
254
  start()
@@ -28,12 +28,14 @@ watchEffect(() => {
28
28
  <style lang="postcss">
29
29
  html.print,
30
30
  html.print body,
31
- html.print #app,
32
- html.print #page-root {
31
+ html.print #app {
33
32
  height: auto;
34
33
  overflow: auto;
35
34
  }
36
-
35
+ html.print #page-root {
36
+ height: auto;
37
+ overflow: hidden;
38
+ }
37
39
  html.print * {
38
40
  -webkit-print-color-adjust: exact;
39
41
  }
@@ -1,13 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import type { RouteRecordRaw } from 'vue-router'
3
- import { computed, reactive } from 'vue'
3
+ import { computed, ref } from 'vue'
4
4
  import { useNav } from '../composables/useNav'
5
5
  import { isClicksDisabled } from '../logic/nav'
6
6
  import PrintSlideClick from './PrintSlideClick.vue'
7
7
 
8
8
  const props = defineProps<{ route: RouteRecordRaw }>()
9
9
 
10
- const clicksElements = reactive(props.route.meta?.__clicksElements || [])
10
+ const clicksElements = ref(props.route.meta?.__clicksElements || [])
11
11
 
12
12
  const route = computed(() => props.route)
13
13
  const nav = useNav(route)
@@ -31,7 +31,7 @@ const loadedRoutes = computed(() => rawRoutes.filter(r => r.meta?.__preloaded ||
31
31
  <GlobalBottom />
32
32
 
33
33
  <!-- Slides -->
34
- <TransitionGroup v-bind="transition">
34
+ <TransitionGroup v-bind="transition" id="slideshow" tag="div">
35
35
  <template v-for="route of loadedRoutes" :key="route.path">
36
36
  <SlideWrapper
37
37
  :is="route?.component as any"
@@ -54,3 +54,13 @@ const loadedRoutes = computed(() => rawRoutes.filter(r => r.meta?.__preloaded ||
54
54
  </template>
55
55
  <PresenterMouse v-if="!isPresenter" />
56
56
  </template>
57
+
58
+ <style scoped>
59
+ #slideshow {
60
+ @apply h-full;
61
+ }
62
+
63
+ #slideshow > div {
64
+ @apply h-full w-full absolute;
65
+ }
66
+ </style>
package/layoutHelper.ts CHANGED
@@ -10,7 +10,7 @@ export function resolveAssetUrl(url: string) {
10
10
  }
11
11
 
12
12
  export function handleBackground(background?: string, dim = false): CSSProperties {
13
- const isColor = background && background[0] === '#' && background.startsWith('rgb')
13
+ const isColor = background && (background[0] === '#' || background.startsWith('rgb'))
14
14
 
15
15
  const style = {
16
16
  background: isColor
@@ -1,6 +1,7 @@
1
1
  import type { App } from 'vue'
2
2
  import { reactive } from 'vue'
3
- import type { UnwrapNestedRefs } from '@vue/reactivity'
3
+ import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
4
+ import type { ComputedRef } from '@vue/reactivity'
4
5
  import type { configs } from '../env'
5
6
  import * as nav from '../logic/nav'
6
7
  import { clicks, route } from '../logic/nav'
@@ -8,16 +9,18 @@ import { isDark } from '../logic/dark'
8
9
  import { injectionSlidevContext } from '../constants'
9
10
  import { useContext } from '../composables/useContext'
10
11
 
11
- export type SlidevContextNavKey = 'route' | 'path' | 'total' | 'currentPage' | 'currentPath' | 'currentRoute' | 'currentSlideId' | 'currentLayout' | 'nextRoute' | 'rawTree' | 'treeWithActiveStatuses' | 'tree' | 'downloadPDF' | 'next' | 'nextSlide' | 'openInEditor' | 'prev' | 'prevSlide'
12
+ export type SlidevContextNavKey = 'path' | 'total' | 'currentPage' | 'currentPath' | 'currentRoute' | 'currentSlideId' | 'currentLayout' | 'nextRoute' | 'rawTree' | 'treeWithActiveStatuses' | 'tree' | 'downloadPDF' | 'next' | 'nextSlide' | 'openInEditor' | 'prev' | 'prevSlide'
12
13
  export type SlidevContextNavClicksKey = 'clicks' | 'clicksElements' | 'clicksTotal' | 'hasNext' | 'hasPrev'
13
14
 
14
- export type SlidevContextNav = Pick<typeof nav, SlidevContextNavKey>
15
+ export interface SlidevContextNav extends Pick<typeof nav, SlidevContextNavKey> {
16
+ route: ComputedRef<RouteRecordRaw | RouteLocationNormalizedLoaded>
17
+ }
15
18
  export type SlidevContextNavClicks = Pick<typeof nav, SlidevContextNavClicksKey>
16
19
 
17
20
  export interface SlidevContext {
18
- nav: UnwrapNestedRefs<SlidevContextNav & SlidevContextNavClicks>
21
+ nav: SlidevContextNav & SlidevContextNavClicks
19
22
  configs: typeof configs
20
- themeConfigs: typeof configs['themeConfig']
23
+ themeConfigs: ComputedRef<typeof configs['themeConfig']>
21
24
  }
22
25
 
23
26
  export default function createSlidevContext() {
@@ -46,7 +46,10 @@ export default function createDirectives() {
46
46
 
47
47
  // Set default dir.value
48
48
  if (dir.value == null)
49
- dir.value = elements?.value.length
49
+ dir.value = elements?.value?.length
50
+ // Relative value starts with '+' o '-'
51
+ if (typeof dir.value === 'string' && (dir.value.startsWith('+') || dir.value.startsWith('-')))
52
+ dir.value = (elements?.value?.length || 0) + Number(dir.value)
50
53
 
51
54
  // If orderMap didn't have dir.value aka the order key, then initialize it.
52
55
  // If key exists, then move current element to the first of order array to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slidev/client",
3
- "version": "0.40.2",
3
+ "version": "0.40.4",
4
4
  "description": "Presentation slides for developers",
5
5
  "author": "antfu <anthonyfu117@hotmail.com>",
6
6
  "license": "MIT",
@@ -16,34 +16,34 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@antfu/utils": "^0.7.2",
19
- "@unocss/reset": "^0.49.4",
20
- "@vueuse/core": "^9.12.0",
21
- "@vueuse/head": "^1.0.24",
22
- "@vueuse/math": "^9.12.0",
19
+ "@unocss/reset": "^0.50.6",
20
+ "@vueuse/core": "^9.13.0",
21
+ "@vueuse/head": "^1.1.23",
22
+ "@vueuse/math": "^9.13.0",
23
23
  "@vueuse/motion": "^2.0.0-beta.27",
24
24
  "codemirror": "^5.65.5",
25
25
  "defu": "^6.1.2",
26
26
  "drauu": "^0.3.2",
27
27
  "file-saver": "^2.0.5",
28
- "js-base64": "^3.7.4",
28
+ "js-base64": "^3.7.5",
29
29
  "js-yaml": "^4.1.0",
30
30
  "katex": "^0.16.4",
31
- "mermaid": "^9.3.0",
31
+ "mermaid": "^9.4.3",
32
32
  "monaco-editor": "^0.33.0",
33
- "nanoid": "^4.0.0",
34
- "prettier": "^2.8.3",
33
+ "nanoid": "^4.0.1",
34
+ "prettier": "^2.8.6",
35
35
  "recordrtc": "^5.6.2",
36
36
  "resolve": "^1.22.1",
37
- "unocss": "^0.49.4",
37
+ "unocss": "^0.50.6",
38
38
  "vite-plugin-windicss": "^1.8.10",
39
39
  "vue": "^3.2.47",
40
40
  "vue-router": "^4.1.6",
41
41
  "vue-starport": "^0.3.0",
42
42
  "windicss": "^3.5.6",
43
- "@slidev/parser": "0.40.2",
44
- "@slidev/types": "0.40.2"
43
+ "@slidev/parser": "0.40.4",
44
+ "@slidev/types": "0.40.4"
45
45
  },
46
46
  "devDependencies": {
47
- "vite": "^4.1.1"
47
+ "vite": "^4.2.1"
48
48
  }
49
49
  }
package/setup/root.ts CHANGED
@@ -11,7 +11,7 @@ import { TRUST_ORIGINS } from '../constants'
11
11
 
12
12
  export default function setupRoot() {
13
13
  // @ts-expect-error injected in runtime
14
-
14
+ // eslint-disable-next-line unused-imports/no-unused-vars
15
15
  const injection_arg = undefined
16
16
 
17
17
  /* __injections__ */
@@ -54,7 +54,7 @@ export default function setupRoot() {
54
54
  const routePath = router.currentRoute.value.path
55
55
  if (!routePath.match(/^\/(\d+|presenter)\/?/))
56
56
  return
57
- if (+state.page !== +currentPage.value || +clicks.value !== +state.clicks) {
57
+ if (state.lastUpdate?.type === 'presenter' && (+state.page !== +currentPage.value || +clicks.value !== +state.clicks)) {
58
58
  router.replace({
59
59
  path: getPath(state.page),
60
60
  query: {
package/shim.d.ts CHANGED
@@ -2,6 +2,8 @@ declare interface Window {
2
2
  // extend the window
3
3
  }
4
4
 
5
+ declare module '*.vue';
6
+
5
7
  // with vite-plugin-vue-markdown, markdowns can be treat as Vue components
6
8
  declare module '*.md' {
7
9
  import type { ComponentOptions } from 'vue'
@@ -1,7 +1,3 @@
1
- #slide-content > div {
2
- @apply h-full w-full absolute;
3
- }
4
-
5
1
  .slidev-layout {
6
2
  @apply px-14 py-10 text-[1.1rem] h-full;
7
3