@slidev/client 0.48.0-beta.2 → 0.48.0-beta.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/Tweet.vue CHANGED
@@ -7,9 +7,9 @@ Usage:
7
7
  -->
8
8
 
9
9
  <script setup lang="ts">
10
- import { useScriptTag } from '@vueuse/core'
11
10
  import { getCurrentInstance, onMounted, ref } from 'vue'
12
11
  import { isDark } from '../logic/dark'
12
+ import { useTweetScript } from '../composables/useTweetScript'
13
13
 
14
14
  const props = defineProps<{
15
15
  id: string | number
@@ -41,21 +41,10 @@ async function create() {
41
41
  }
42
42
 
43
43
  // @ts-expect-error global
44
- if (window?.twttr?.widgets) {
44
+ if (window?.twttr?.widgets)
45
45
  onMounted(create)
46
- }
47
- else {
48
- useScriptTag(
49
- 'https://platform.twitter.com/widgets.js',
50
- () => {
51
- if (vm.isMounted)
52
- create()
53
- else
54
- onMounted(create, vm)
55
- },
56
- { async: true },
57
- )
58
- }
46
+ else
47
+ useTweetScript(vm, create)
59
48
  </script>
60
49
 
61
50
  <template>
@@ -96,7 +96,7 @@ export default defineComponent({
96
96
  if (depth < +this.depth && Array.isArray(i.children))
97
97
  vNode = h(i, {}, mapSubList(i.children, depth))
98
98
  else
99
- vNode = i
99
+ vNode = h(i)
100
100
 
101
101
  const delta = thisShowIdx - execIdx
102
102
  execIdx = thisShowIdx
@@ -0,0 +1,17 @@
1
+ import { createSharedComposable, useScriptTag } from '@vueuse/core'
2
+ import type { ComponentInternalInstance } from 'vue'
3
+ import { onMounted } from 'vue'
4
+
5
+ export const useTweetScript = createSharedComposable(
6
+ (vm: ComponentInternalInstance, create: () => void) =>
7
+ useScriptTag(
8
+ 'https://platform.twitter.com/widgets.js',
9
+ () => {
10
+ if (vm.isMounted)
11
+ create()
12
+ else
13
+ onMounted(create, vm)
14
+ },
15
+ { async: true },
16
+ ),
17
+ )
package/constants.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ComputedRef, InjectionKey, Ref, UnwrapNestedRefs } from 'vue'
2
2
  import type { RouteRecordRaw } from 'vue-router'
3
3
  import type { ClicksContext, RenderContext } from '@slidev/types'
4
+ import { objectOmit } from '@vueuse/core'
4
5
  import type { SlidevContext } from './modules/context'
5
6
 
6
7
  export const injectionClicksContext: InjectionKey<Ref<ClicksContext>> = Symbol('slidev-clicks-context')
@@ -24,3 +25,56 @@ export const TRUST_ORIGINS = [
24
25
  'localhost',
25
26
  '127.0.0.1',
26
27
  ]
28
+
29
+ const FRONTMATTER_FIELDS = [
30
+ 'clicks',
31
+ 'disabled',
32
+ 'hide',
33
+ 'hideInToc',
34
+ 'layout',
35
+ 'level',
36
+ 'preload',
37
+ 'routeAlias',
38
+ 'src',
39
+ 'title',
40
+ 'transition',
41
+ 'zoom',
42
+ ]
43
+
44
+ const HEADMATTER_FIELDS = [
45
+ ...FRONTMATTER_FIELDS,
46
+ 'theme',
47
+ 'titleTemplate',
48
+ 'info',
49
+ 'author',
50
+ 'keywords',
51
+ 'presenter',
52
+ 'download',
53
+ 'exportFilename',
54
+ 'export',
55
+ 'highlighter',
56
+ 'lineNumbers',
57
+ 'monaco',
58
+ 'remoteAssets',
59
+ 'selectable',
60
+ 'record',
61
+ 'colorSchema',
62
+ 'routerMode',
63
+ 'aspectRatio',
64
+ 'canvasWidth',
65
+ 'themeConfig',
66
+ 'favicon',
67
+ 'plantUmlServer',
68
+ 'fonts',
69
+ 'defaults',
70
+ 'drawings',
71
+ 'htmlAttrs',
72
+ 'mdc',
73
+ ]
74
+
75
+ export function filterFrontmatter(frontmatter: Record<string, any>, pageNo: number) {
76
+ return {
77
+ ...objectOmit(frontmatter, pageNo === 0 ? HEADMATTER_FIELDS : FRONTMATTER_FIELDS),
78
+ frontmatter,
79
+ }
80
+ }
@@ -1,4 +1,6 @@
1
1
  <script setup lang="ts">
2
+ import { Menu } from 'floating-vue'
3
+ import 'floating-vue/dist/style.css'
2
4
  import {
3
5
  brush,
4
6
  brushColors,
@@ -21,19 +23,24 @@ function undo() {
21
23
  function redo() {
22
24
  drauu.redo()
23
25
  }
26
+
27
+ let lastDrawingMode: typeof drawingMode.value = 'stylus'
24
28
  function setDrawingMode(mode: typeof drawingMode.value) {
25
29
  drawingMode.value = mode
26
30
  drawingEnabled.value = true
31
+ if (mode !== 'eraseLine')
32
+ lastDrawingMode = mode
27
33
  }
28
34
  function setBrushColor(color: typeof brush.color) {
29
35
  brush.color = color
30
36
  drawingEnabled.value = true
37
+ drawingMode.value = lastDrawingMode
31
38
  }
32
39
  </script>
33
40
 
34
41
  <template>
35
42
  <Draggable
36
- class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200"
43
+ class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200 z-20"
37
44
  dark="border border-gray-400 border-opacity-10"
38
45
  :class="drawingEnabled ? '' : drawingPinned ? 'opacity-40 hover:opacity-90' : 'opacity-0 pointer-events-none'"
39
46
  storage-key="slidev-drawing-pos"
@@ -57,23 +64,41 @@ function setBrushColor(color: typeof brush.color) {
57
64
  <IconButton title="Draw a rectangle" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
58
65
  <carbon:checkbox />
59
66
  </IconButton>
60
- <!-- TODO: not sure why it's not working! -->
61
- <!-- <IconButton title="Erase" :class="{ shallow: drawingMode != 'eraseLine' }" @click="setDrawingMode('eraseLine')">
67
+ <IconButton title="Erase" :class="{ shallow: drawingMode !== 'eraseLine' }" @click="setDrawingMode('eraseLine')">
62
68
  <carbon:erase />
63
- </IconButton> -->
69
+ </IconButton>
64
70
 
65
71
  <VerticalDivider />
66
72
 
73
+ <Menu>
74
+ <IconButton title="Adjust stroke width" :class="{ shallow: drawingMode === 'eraseLine' }">
75
+ <svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
76
+ <line x1="2" y1="15" x2="22" y2="4" stroke="currentColor" stroke-width="1" stroke-linecap="round" />
77
+ <line x1="2" y1="24" x2="28" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
78
+ <line x1="7" y1="31" x2="29" y2="19" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
79
+ </svg>
80
+ </IconButton>
81
+ <template #popper>
82
+ <div class="flex bg-main p-2">
83
+ <div class="inline-block w-7 text-center">
84
+ {{ brush.size }}
85
+ </div>
86
+ <div class="pt-.5">
87
+ <input v-model="brush.size" type="range" min="1" max="15" @change="drawingMode = lastDrawingMode">
88
+ </div>
89
+ </div>
90
+ </template>
91
+ </Menu>
67
92
  <IconButton
68
93
  v-for="color of brushColors"
69
94
  :key="color"
70
95
  title="Set brush color"
71
- :class="brush.color === color ? 'active' : 'shallow'"
96
+ :class="brush.color === color && drawingMode !== 'eraseLine' ? 'active' : 'shallow'"
72
97
  @click="setBrushColor(color)"
73
98
  >
74
99
  <div
75
- class="w-6 h-6 transition-all transform border border-gray-400/50"
76
- :class="brush.color !== color ? 'rounded-1/2 scale-85' : 'rounded-md'"
100
+ class="w-6 h-6 transition-all transform border"
101
+ :class="brush.color !== color ? 'rounded-1/2 scale-85 border-white' : 'rounded-md border-gray-300/50'"
77
102
  :style="drawingEnabled ? { background: color } : { borderColor: color }"
78
103
  />
79
104
  </IconButton>
@@ -87,7 +112,7 @@ function setBrushColor(color: typeof brush.color) {
87
112
  <carbon:redo />
88
113
  </IconButton>
89
114
  <IconButton title="Delete" :class="{ disabled: !canClear }" @click="clearDrauu()">
90
- <carbon:delete />
115
+ <carbon:trash-can />
91
116
  </IconButton>
92
117
 
93
118
  <VerticalDivider />
@@ -106,3 +131,11 @@ function setBrushColor(color: typeof brush.color) {
106
131
  </IconButton>
107
132
  </Draggable>
108
133
  </template>
134
+
135
+ <style lang="postcss">
136
+ .v-popper--theme-menu {
137
+ .v-popper__arrow-inner {
138
+ @apply border-main;
139
+ }
140
+ }
141
+ </style>
@@ -38,7 +38,6 @@ watch(
38
38
  async function save() {
39
39
  dirty.value = false
40
40
  await update({
41
- raw: null!,
42
41
  note: note.value || undefined,
43
42
  content: content.value,
44
43
  // frontmatter: frontmatter.value,
@@ -10,7 +10,7 @@ const props = defineProps({
10
10
  },
11
11
  })
12
12
 
13
- const emit = defineEmits<{ (name: 'modelValue', v: boolean): void }>()
13
+ const emit = defineEmits(['update:modelValue'])
14
14
  const value = useVModel(props, 'modelValue', emit)
15
15
  const hasInfo = computed(() => typeof configs.info === 'string')
16
16
  </script>
@@ -10,7 +10,7 @@ const props = defineProps({
10
10
  },
11
11
  })
12
12
 
13
- const emit = defineEmits<{ (name: 'modelValue', v: boolean): void }>()
13
+ const emit = defineEmits(['update:modelValue'])
14
14
  const value = useVModel(props, 'modelValue', emit)
15
15
 
16
16
  function onClick() {
@@ -36,7 +36,7 @@ const { ignoreUpdates } = ignorableWatch(
36
36
  const id = currentSlideId.value
37
37
  clearTimeout(timer)
38
38
  timer = setTimeout(() => {
39
- update({ raw: null!, note: v }, id)
39
+ update({ note: v }, id)
40
40
  }, 500)
41
41
  },
42
42
  )
@@ -1,6 +1,7 @@
1
- import { defineComponent, h, provide, ref, toRef } from 'vue'
1
+ import { computed, defineComponent, h, provide, ref, toRef } from 'vue'
2
2
  import type { PropType } from 'vue'
3
3
  import type { ClicksContext, RenderContext } from '@slidev/types'
4
+ import type { RouteRecordRaw } from 'vue-router'
4
5
  import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
5
6
 
6
7
  export default defineComponent({
@@ -20,23 +21,37 @@ export default defineComponent({
20
21
  },
21
22
  is: {
22
23
  type: Object,
23
- default: undefined,
24
+ required: true,
24
25
  },
25
26
  route: {
26
- type: Object,
27
- default: undefined,
27
+ type: Object as PropType<RouteRecordRaw>,
28
+ required: true,
28
29
  },
29
30
  },
30
31
  setup(props) {
31
- provide(injectionRoute, props.route as any)
32
- provide(injectionCurrentPage, ref(+props.route?.path))
32
+ provide(injectionRoute, props.route)
33
+ provide(injectionCurrentPage, ref(+props.route.path))
33
34
  provide(injectionRenderContext, ref(props.renderContext as RenderContext))
34
35
  provide(injectionActive, toRef(props, 'active'))
35
36
  provide(injectionClicksContext, toRef(props, 'clicksContext'))
37
+
38
+ const style = computed(() => {
39
+ const zoom = props.route.meta?.slide?.frontmatter.zoom ?? 1
40
+ return zoom === 1
41
+ ? undefined
42
+ : {
43
+ width: `${100 / zoom}%`,
44
+ height: `${100 / zoom}%`,
45
+ transformOrigin: 'top left',
46
+ transform: `scale(${zoom})`,
47
+ }
48
+ })
49
+
50
+ return {
51
+ style,
52
+ }
36
53
  },
37
54
  render() {
38
- if (this.$props.is)
39
- return h(this.$props.is)
40
- return this.$slots?.default?.()
55
+ return h(this.$props.is, { style: this.style })
41
56
  },
42
57
  })
@@ -14,7 +14,7 @@ import IconButton from './IconButton.vue'
14
14
 
15
15
  const props = defineProps<{ modelValue: boolean }>()
16
16
 
17
- const emit = defineEmits([])
17
+ const emit = defineEmits(['update:modelValue'])
18
18
  const value = useVModel(props, 'modelValue', emit)
19
19
 
20
20
  function close() {
package/logic/drawings.ts CHANGED
@@ -4,6 +4,7 @@ import { createDrauu } from 'drauu'
4
4
  import { toReactive, useLocalStorage } from '@vueuse/core'
5
5
  import { drawingState, onPatch, patch } from '../state/drawings'
6
6
  import { configs } from '../env'
7
+ import { isInputting } from '../state'
7
8
  import { currentPage, isPresenter } from './nav'
8
9
 
9
10
  export const brushColors = [
@@ -40,11 +41,13 @@ export const drawingMode = computed({
40
41
  set(v: DrawingMode | 'arrow') {
41
42
  _mode.value = v
42
43
  if (v === 'arrow') {
43
- brush.mode = 'line'
44
+ // eslint-disable-next-line ts/no-use-before-define
45
+ drauu.mode = 'line'
44
46
  brush.arrowEnd = true
45
47
  }
46
48
  else {
47
- brush.mode = v
49
+ // eslint-disable-next-line ts/no-use-before-define
50
+ drauu.mode = v
48
51
  brush.arrowEnd = false
49
52
  }
50
53
  },
@@ -110,7 +113,7 @@ drauu.on('start', () => isDrawing.value = true)
110
113
  drauu.on('end', () => isDrawing.value = false)
111
114
 
112
115
  window.addEventListener('keydown', (e) => {
113
- if (!drawingEnabled.value)
116
+ if (!drawingEnabled.value || isInputting.value)
114
117
  return
115
118
 
116
119
  const noModifier = !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey
package/logic/nav.ts CHANGED
@@ -177,7 +177,7 @@ export async function downloadPDF() {
177
177
  export async function openInEditor(url?: string) {
178
178
  if (url == null) {
179
179
  const slide = currentRoute.value?.meta?.slide
180
- if (!slide?.filepath)
180
+ if (!slide)
181
181
  return false
182
182
  url = `${slide.filepath}:${slide.start}`
183
183
  }
package/logic/note.ts CHANGED
@@ -2,17 +2,17 @@ import type { MaybeRef } from '@vueuse/core'
2
2
  import { useFetch } from '@vueuse/core'
3
3
  import type { Ref } from 'vue'
4
4
  import { computed, ref, unref } from 'vue'
5
- import type { SlideInfo, SlideInfoExtended } from '@slidev/types'
5
+ import type { SlideInfo } from '@slidev/types'
6
6
 
7
7
  export interface UseSlideInfo {
8
- info: Ref<SlideInfoExtended | undefined>
9
- update: (data: Partial<SlideInfo>) => Promise<SlideInfoExtended | void>
8
+ info: Ref<SlideInfo | undefined>
9
+ update: (data: Partial<SlideInfo>) => Promise<SlideInfo | void>
10
10
  }
11
11
 
12
12
  export function useSlideInfo(id: number | undefined): UseSlideInfo {
13
13
  if (id == null) {
14
14
  return {
15
- info: ref() as Ref<SlideInfoExtended | undefined>,
15
+ info: ref() as Ref<SlideInfo | undefined>,
16
16
  update: async () => {},
17
17
  }
18
18
  }
@@ -41,7 +41,7 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
41
41
  info.value = payload.data
42
42
  })
43
43
  import.meta.hot?.on('slidev-update-note', (payload) => {
44
- if (payload.id === id && info.value.note.trim() !== payload.note.trim())
44
+ if (payload.id === id && info.value.note?.trim() !== payload.note?.trim())
45
45
  info.value = { ...info.value, ...payload }
46
46
  })
47
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slidev/client",
3
- "version": "0.48.0-beta.2",
3
+ "version": "0.48.0-beta.4",
4
4
  "description": "Presentation slides for developers",
5
5
  "author": "antfu <anthonyfu117@hotmail.com>",
6
6
  "license": "MIT",
@@ -22,15 +22,15 @@
22
22
  "@antfu/utils": "^0.7.7",
23
23
  "@iconify-json/carbon": "^1.1.30",
24
24
  "@iconify-json/ph": "^1.1.11",
25
- "@shikijs/vitepress-twoslash": "^1.1.3",
25
+ "@shikijs/vitepress-twoslash": "^1.1.6",
26
26
  "@unhead/vue": "^1.8.10",
27
27
  "@unocss/reset": "^0.58.5",
28
- "@vueuse/core": "^10.7.2",
29
- "@vueuse/math": "^10.7.2",
28
+ "@vueuse/core": "^10.8.0",
29
+ "@vueuse/math": "^10.8.0",
30
30
  "@vueuse/motion": "^2.0.0",
31
31
  "codemirror": "^5.65.16",
32
32
  "defu": "^6.1.4",
33
- "drauu": "^0.3.7",
33
+ "drauu": "^0.4.0",
34
34
  "file-saver": "^2.0.5",
35
35
  "floating-vue": "^5.2.2",
36
36
  "fuse.js": "^7.0.0",
@@ -39,15 +39,15 @@
39
39
  "katex": "^0.16.9",
40
40
  "mermaid": "^10.8.0",
41
41
  "monaco-editor": "^0.37.1",
42
- "nanoid": "^5.0.5",
42
+ "nanoid": "^5.0.6",
43
43
  "prettier": "^3.2.5",
44
44
  "recordrtc": "^5.6.2",
45
45
  "resolve": "^1.22.8",
46
46
  "unocss": "^0.58.5",
47
47
  "vue": "^3.4.19",
48
48
  "vue-router": "^4.2.5",
49
- "@slidev/parser": "0.48.0-beta.2",
50
- "@slidev/types": "0.48.0-beta.2"
49
+ "@slidev/parser": "0.48.0-beta.4",
50
+ "@slidev/types": "0.48.0-beta.4"
51
51
  },
52
52
  "devDependencies": {
53
53
  "vite": "^5.1.3"
package/routes.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
2
2
  import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
3
3
  import type { TransitionGroupProps } from 'vue'
4
- import type { ClicksContext } from '@slidev/types'
5
- import Play from './internals/Play.vue'
6
- import Print from './internals/Print.vue'
4
+ import type { ClicksContext, SlideInfo } from '@slidev/types'
7
5
 
8
6
  // @ts-expect-error missing types
9
7
  import _rawRoutes, { redirects } from '/@slidev/routes'
@@ -17,13 +15,13 @@ export const routes: RouteRecordRaw[] = [
17
15
  {
18
16
  name: 'play',
19
17
  path: '/',
20
- component: Play,
18
+ component: () => import('./internals/Play.vue'),
21
19
  children: [
22
20
  ...rawRoutes,
23
21
  ...redirects,
24
22
  ],
25
23
  },
26
- { name: 'print', path: '/print', component: Print },
24
+ { name: 'print', path: '/print', component: () => import('./internals/Print.vue') },
27
25
  { path: '', redirect: { path: '/1' } },
28
26
  { path: '/:pathMatch(.*)', redirect: { path: '/1' } },
29
27
  ]
@@ -86,19 +84,12 @@ declare module 'vue-router' {
86
84
  preload?: boolean
87
85
 
88
86
  // slide info
89
- slide?: {
87
+ slide?: Omit<SlideInfo, 'source'> & {
88
+ noteHTML: string
89
+ filepath: string
90
90
  start: number
91
- end: number
92
- note?: string
93
- noteHTML?: string
94
91
  id: number
95
92
  no: number
96
- filepath: string
97
- title?: string
98
- level?: number
99
- raw: string
100
- content: string
101
- frontmatter: Record<string, any>
102
93
  }
103
94
 
104
95
  // private fields
@@ -1,4 +1,5 @@
1
1
  import type { Ref, WritableComputedRef } from 'vue'
2
+ import { onClickOutside } from '@vueuse/core'
2
3
  import { watch } from 'vue'
3
4
  import * as _CodeMirror from 'codemirror'
4
5
  import 'codemirror/mode/javascript/javascript'
@@ -47,5 +48,11 @@ export async function useCodeMirror(
47
48
  { immediate: true },
48
49
  )
49
50
 
51
+ onClickOutside(cm.getWrapperElement(), () => {
52
+ const el = cm.getInputField()
53
+ if (document.activeElement === el)
54
+ el.blur()
55
+ })
56
+
50
57
  return cm
51
58
  }