@slidev/client 0.48.0-beta.8 → 0.48.0

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.
Files changed (98) hide show
  1. package/App.vue +7 -0
  2. package/builtin/Arrow.vue +2 -4
  3. package/builtin/CodeBlockWrapper.vue +33 -28
  4. package/builtin/KaTexBlockWrapper.vue +1 -1
  5. package/builtin/Link.vue +3 -1
  6. package/builtin/Mermaid.vue +4 -3
  7. package/builtin/Monaco.vue +166 -93
  8. package/builtin/ShikiMagicMove.vue +103 -0
  9. package/builtin/SlidevVideo.vue +1 -1
  10. package/builtin/Toc.vue +1 -1
  11. package/builtin/TocList.vue +4 -3
  12. package/builtin/Tweet.vue +12 -9
  13. package/builtin/VClick.ts +2 -1
  14. package/composables/useClicks.ts +19 -32
  15. package/composables/useDarkMode.ts +9 -0
  16. package/composables/useDrawings.ts +181 -0
  17. package/composables/useNav.ts +346 -44
  18. package/{logic/note.ts → composables/useSlideInfo.ts} +13 -16
  19. package/composables/useSwipeControls.ts +43 -0
  20. package/composables/useTocTree.ts +81 -0
  21. package/composables/useViewTransition.ts +7 -4
  22. package/constants.ts +4 -3
  23. package/context.ts +13 -6
  24. package/env.ts +7 -16
  25. package/index.html +1 -0
  26. package/index.ts +12 -0
  27. package/internals/ClicksSlider.vue +93 -0
  28. package/internals/CodeRunner.vue +142 -0
  29. package/internals/Controls.vue +2 -2
  30. package/internals/DomElement.vue +18 -0
  31. package/internals/DrawingControls.vue +15 -17
  32. package/internals/DrawingLayer.vue +6 -5
  33. package/internals/DrawingPreview.vue +4 -2
  34. package/internals/Goto.vue +9 -6
  35. package/internals/IconButton.vue +7 -3
  36. package/internals/NavControls.vue +31 -12
  37. package/internals/NoteDisplay.vue +131 -8
  38. package/internals/NoteEditable.vue +129 -0
  39. package/internals/NoteStatic.vue +8 -6
  40. package/internals/PrintContainer.vue +11 -8
  41. package/internals/PrintSlide.vue +11 -12
  42. package/internals/PrintSlideClick.vue +14 -19
  43. package/internals/{SlidesOverview.vue → QuickOverview.vue} +35 -22
  44. package/internals/RecordingControls.vue +1 -1
  45. package/internals/RecordingDialog.vue +5 -6
  46. package/internals/{Editor.vue → SideEditor.vue} +26 -17
  47. package/internals/SlideContainer.vue +13 -9
  48. package/internals/SlideLoading.vue +19 -0
  49. package/internals/SlideWrapper.vue +79 -0
  50. package/internals/SlidesShow.vue +36 -22
  51. package/layouts/error.vue +5 -0
  52. package/layouts/two-cols-header.vue +9 -3
  53. package/logic/overview.ts +2 -2
  54. package/logic/route.ts +16 -5
  55. package/logic/slides.ts +20 -0
  56. package/logic/transition.ts +50 -0
  57. package/logic/utils.ts +24 -1
  58. package/main.ts +3 -15
  59. package/{setup → modules}/codemirror.ts +1 -3
  60. package/modules/context.ts +1 -46
  61. package/modules/mermaid.ts +9 -8
  62. package/package.json +21 -15
  63. package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
  64. package/{internals/NotesView.vue → pages/notes.vue} +9 -6
  65. package/pages/overview.vue +231 -0
  66. package/{internals/Play.vue → pages/play.vue} +22 -15
  67. package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +15 -8
  68. package/{internals/Presenter.vue → pages/presenter.vue} +129 -107
  69. package/{internals/Print.vue → pages/print.vue} +6 -5
  70. package/routes.ts +26 -57
  71. package/setup/code-runners.ts +164 -0
  72. package/setup/main.ts +39 -9
  73. package/setup/mermaid.ts +5 -6
  74. package/setup/monaco.ts +114 -51
  75. package/setup/root.ts +62 -18
  76. package/setup/shortcuts.ts +15 -12
  77. package/shim-vue.d.ts +34 -0
  78. package/shim.d.ts +1 -13
  79. package/state/index.ts +2 -2
  80. package/styles/code.css +9 -5
  81. package/styles/index.css +63 -7
  82. package/styles/katex.css +1 -1
  83. package/styles/layouts-base.css +17 -12
  84. package/styles/shiki-twoslash.css +1 -1
  85. package/styles/vars.css +1 -0
  86. package/uno.config.ts +14 -2
  87. package/utils.ts +15 -2
  88. package/composables/useContext.ts +0 -17
  89. package/composables/useTweetScript.ts +0 -17
  90. package/iframes/monaco/index.css +0 -28
  91. package/iframes/monaco/index.html +0 -7
  92. package/iframes/monaco/index.ts +0 -260
  93. package/internals/NoteEditor.vue +0 -88
  94. package/internals/SlideWrapper.ts +0 -58
  95. package/logic/drawings.ts +0 -161
  96. package/logic/nav.ts +0 -278
  97. package/setup/prettier.ts +0 -43
  98. /package/{composables → logic}/hmr.ts +0 -0
@@ -0,0 +1,164 @@
1
+ import { createSingletonPromise } from '@antfu/utils'
2
+ import type { CodeRunner, CodeRunnerContext, CodeRunnerOutput, CodeRunnerOutputText, CodeRunnerOutputs } from '@slidev/types'
3
+ import type { CodeToHastOptions } from 'shiki'
4
+ import { isDark } from '../logic/dark'
5
+ import setups from '#slidev/setups/code-runners'
6
+
7
+ export default createSingletonPromise(async () => {
8
+ const runners: Record<string, CodeRunner> = {
9
+ javascript: runJavaScript,
10
+ js: runJavaScript,
11
+ typescript: runTypeScript,
12
+ ts: runTypeScript,
13
+ }
14
+
15
+ const { shiki, themes } = await import('#slidev/shiki')
16
+ const highlighter = await shiki
17
+ const highlight = (code: string, lang: string, options: Partial<CodeToHastOptions> = {}) => highlighter.codeToHtml(code, {
18
+ lang,
19
+ theme: typeof themes === 'string'
20
+ ? themes
21
+ : isDark.value
22
+ ? themes.dark || 'vitesse-dark'
23
+ : themes.light || 'vitesse-light',
24
+ ...options,
25
+ })
26
+
27
+ const run = async (code: string, lang: string, options: Record<string, unknown>): Promise<CodeRunnerOutputs> => {
28
+ try {
29
+ const runner = runners[lang]
30
+ if (!runner)
31
+ throw new Error(`Runner for language "${lang}" not found`)
32
+ return await runner(
33
+ code,
34
+ {
35
+ options,
36
+ highlight,
37
+ run: async (code, lang) => {
38
+ return await run(code, lang, options)
39
+ },
40
+ },
41
+ )
42
+ }
43
+ catch (e) {
44
+ console.error(e)
45
+ return {
46
+ error: `${e}`,
47
+ }
48
+ }
49
+ }
50
+
51
+ for (const setup of setups) {
52
+ const result = await setup(runners)
53
+ Object.assign(runners, result)
54
+ }
55
+
56
+ return {
57
+ highlight,
58
+ run,
59
+ }
60
+ })
61
+
62
+ // Ported from https://github.com/microsoft/TypeScript-Website/blob/v2/packages/playground/src/sidebar/runtime.ts
63
+ export async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
64
+ const allLogs: CodeRunnerOutput[] = []
65
+
66
+ const replace = {} as any
67
+ const logger = function (...objs: any[]) {
68
+ allLogs.push(objs.map(printObject))
69
+ }
70
+ replace.info = replace.log = replace.debug = replace.warn = replace.error = logger
71
+ replace.clear = () => allLogs.length = 0
72
+ const vmConsole = Object.assign({}, console, replace)
73
+ try {
74
+ const safeJS = `return async (console) => {${sanitizeJS(code)}}`
75
+ // eslint-disable-next-line no-new-func
76
+ await (new Function(safeJS)())(vmConsole)
77
+ }
78
+ catch (error) {
79
+ return {
80
+ error: `ERROR: ${error}`,
81
+ }
82
+ }
83
+
84
+ function printObject(arg: any): CodeRunnerOutputText {
85
+ if (typeof arg === 'string') {
86
+ return {
87
+ text: arg,
88
+ }
89
+ }
90
+ return {
91
+ text: objectToText(arg),
92
+ highlightLang: 'javascript',
93
+ }
94
+ }
95
+
96
+ function objectToText(arg: any): string {
97
+ let textRep = ''
98
+ if (arg instanceof Error) {
99
+ textRep = `Error: ${JSON.stringify(arg.message)}`
100
+ }
101
+ else if (arg === null || arg === undefined || typeof arg === 'symbol') {
102
+ textRep = String(arg)
103
+ }
104
+ else if (Array.isArray(arg)) {
105
+ textRep = `[${arg.map(objectToText).join(', ')}]`
106
+ }
107
+ else if (arg instanceof Set) {
108
+ const setIter = [...arg]
109
+ textRep = `Set (${arg.size}) {${setIter.map(objectToText).join(', ')}}`
110
+ }
111
+ else if (arg instanceof Map) {
112
+ const mapIter = [...arg.entries()]
113
+ textRep
114
+ = `Map (${arg.size}) {${mapIter
115
+ .map(([k, v]) => `${objectToText(k)} => ${objectToText(v)}`)
116
+ .join(', ')
117
+ }}`
118
+ }
119
+ else if (arg instanceof RegExp) {
120
+ textRep = arg.toString()
121
+ }
122
+ else if (typeof arg === 'string') {
123
+ textRep = JSON.stringify(arg)
124
+ }
125
+ else if (typeof arg === 'object') {
126
+ const name = arg.constructor?.name ?? ''
127
+ // No one needs to know an obj is an obj
128
+ const nameWithoutObject = name && name === 'Object' ? '' : name
129
+ const prefix = nameWithoutObject ? `${nameWithoutObject}: ` : ''
130
+
131
+ // JSON.stringify omits any keys with a value of undefined. To get around this, we replace undefined with the text __undefined__ and then do a global replace using regex back to keyword undefined
132
+ textRep
133
+ = prefix
134
+ + JSON.stringify(arg, (_, value) => (value === undefined ? '__undefined__' : value), 2).replace(
135
+ /"__undefined__"/g,
136
+ 'undefined',
137
+ )
138
+
139
+ textRep = String(textRep)
140
+ }
141
+ else {
142
+ textRep = String(arg)
143
+ }
144
+ return textRep
145
+ }
146
+
147
+ // The reflect-metadata runtime is available, so allow that to go through
148
+ function sanitizeJS(code: string) {
149
+ return code.replace(`import "reflect-metadata"`, '').replace(`require("reflect-metadata")`, '')
150
+ }
151
+
152
+ return allLogs
153
+ }
154
+
155
+ let tsModule: typeof import('typescript') | undefined
156
+
157
+ export async function runTypeScript(code: string, context: CodeRunnerContext) {
158
+ const { transpile } = tsModule ??= await import('typescript')
159
+ code = transpile(code, {
160
+ module: tsModule.ModuleKind.ESNext,
161
+ target: tsModule.ScriptTarget.ES2022,
162
+ })
163
+ return await context.run(code, 'javascript')
164
+ }
package/setup/main.ts CHANGED
@@ -1,10 +1,20 @@
1
- /* __imports__ */
2
-
3
1
  import type { AppContext } from '@slidev/types'
4
2
  import { MotionPlugin } from '@vueuse/motion'
5
3
  import TwoSlashFloatingVue from '@shikijs/vitepress-twoslash/client'
4
+ import type { App } from 'vue'
5
+ import { nextTick } from 'vue'
6
+ import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
7
+ import { createHead } from '@unhead/vue'
8
+ import { routeForceRefresh } from '../logic/route'
9
+ import { createVClickDirectives } from '../modules/v-click'
10
+ import { createVMarkDirective } from '../modules/v-mark'
11
+ import { routes } from '../routes'
12
+ import setups from '#slidev/setups/main'
13
+
14
+ import '#slidev/styles'
15
+ import 'shiki-magic-move/style.css'
6
16
 
7
- export default function setupMain(context: AppContext) {
17
+ export default async function setupMain(app: App) {
8
18
  function setMaxHeight() {
9
19
  // disable the mobile navbar scroll
10
20
  // see https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
@@ -13,12 +23,32 @@ export default function setupMain(context: AppContext) {
13
23
  setMaxHeight()
14
24
  window.addEventListener('resize', setMaxHeight)
15
25
 
16
- context.app.use(MotionPlugin)
17
- context.app.use(TwoSlashFloatingVue as any)
26
+ const router = createRouter({
27
+ history: __SLIDEV_HASH_ROUTE__
28
+ ? createWebHashHistory(import.meta.env.BASE_URL)
29
+ : createWebHistory(import.meta.env.BASE_URL),
30
+ routes,
31
+ })
32
+
33
+ app.use(router)
34
+ app.use(createHead())
35
+ app.use(createVClickDirectives())
36
+ app.use(createVMarkDirective())
37
+ app.use(MotionPlugin)
38
+ app.use(TwoSlashFloatingVue as any, { container: '#slideshow' })
39
+
40
+ const context: AppContext = {
41
+ app,
42
+ router,
43
+ }
18
44
 
19
- // @ts-expect-error inject in runtime
20
- // eslint-disable-next-line unused-imports/no-unused-vars
21
- const injection_arg = context
45
+ nextTick(() => {
46
+ router.afterEach(async () => {
47
+ await nextTick()
48
+ routeForceRefresh.value += 1
49
+ })
50
+ })
22
51
 
23
- /* __injections__ */
52
+ for (const setup of setups)
53
+ await setup(context)
24
54
  }
package/setup/mermaid.ts CHANGED
@@ -1,15 +1,14 @@
1
- /* __imports__ */
2
-
3
1
  import type { MermaidOptions } from '@slidev/types'
4
2
  import { defineMermaidSetup } from '@slidev/types'
3
+ import setups from '#slidev/setups/mermaid'
5
4
 
6
5
  export default defineMermaidSetup(() => {
7
- // eslint-disable-next-line prefer-const
8
- let injection_return: MermaidOptions = {
6
+ const setupReturn: MermaidOptions = {
9
7
  theme: 'default',
10
8
  }
11
9
 
12
- /* __injections__ */
10
+ for (const setup of setups)
11
+ Object.assign(setupReturn, setup())
13
12
 
14
- return injection_return
13
+ return setupReturn
15
14
  })
package/setup/monaco.ts CHANGED
@@ -1,70 +1,133 @@
1
- import { getCurrentInstance, onMounted } from 'vue'
2
- import * as monaco from 'monaco-editor'
3
1
  import { createSingletonPromise } from '@antfu/utils'
4
2
  import type { MonacoSetupReturn } from '@slidev/types'
3
+ import * as monaco from 'monaco-editor'
4
+ import { watchEffect } from 'vue'
5
+ import { setupTypeAcquisition } from '@typescript/ata'
6
+ import ts from 'typescript'
7
+
8
+ import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
9
+ import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
10
+ import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
11
+ import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
12
+ import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
13
+
14
+ // @ts-expect-error missing types
15
+ import { ContextViewService } from 'monaco-editor/esm/vs/platform/contextview/browser/contextViewService'
16
+
17
+ // @ts-expect-error missing types
18
+ import { SyncDescriptor } from 'monaco-editor/esm/vs/platform/instantiation/common/descriptors'
5
19
 
6
- /* __imports__ */
20
+ // @ts-expect-error missing types
21
+ import { StandaloneServices } from 'monaco-editor/esm/vs/editor/standalone/browser/standaloneServices'
22
+
23
+ import { isDark } from '../logic/dark'
24
+ import configs from '#slidev/configs'
25
+ import setups from '#slidev/setups/monaco'
26
+
27
+ window.MonacoEnvironment = {
28
+ getWorker(_, label) {
29
+ if (label === 'json')
30
+ return new JsonWorker()
31
+ if (label === 'css' || label === 'scss' || label === 'less')
32
+ return new CssWorker()
33
+ if (label === 'html' || label === 'handlebars' || label === 'razor')
34
+ return new HtmlWorker()
35
+ if (label === 'typescript' || label === 'javascript')
36
+ return new TsWorker()
37
+ return new EditorWorker()
38
+ },
39
+ }
40
+
41
+ class ContextViewService2 extends ContextViewService {
42
+ showContextView(...args: any) {
43
+ super.showContextView(...args)
44
+ // @ts-expect-error missing types
45
+ const contextView = this.contextView.view as HTMLElement
46
+ contextView.style.left = `calc(${contextView.style.left} / var(--slidev-slide-scale))`
47
+ contextView.style.top = `calc(${contextView.style.top} / var(--slidev-slide-scale))`
48
+ // Reset the scale to 1. Otherwise, the sub-menu will be in the wrong position.
49
+ contextView.style.transform = `scale(calc(1 / var(--slidev-slide-scale)))`
50
+ contextView.style.transformOrigin = '0 0'
51
+ }
52
+ }
7
53
 
8
54
  const setup = createSingletonPromise(async () => {
9
- monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
10
- ...monaco.languages.typescript.typescriptDefaults.getCompilerOptions(),
11
- noUnusedLocals: false,
12
- noUnusedParameters: false,
13
- allowUnreachableCode: true,
14
- allowUnusedLabels: true,
55
+ // Initialize services first, otherwise we can't override them.
56
+ StandaloneServices.initialize({
57
+ contextViewService: new SyncDescriptor(ContextViewService2, [], true),
58
+ })
59
+
60
+ const defaults = monaco.languages.typescript.typescriptDefaults
61
+
62
+ defaults.setCompilerOptions({
63
+ ...defaults.getCompilerOptions(),
15
64
  strict: true,
65
+ moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
66
+ module: monaco.languages.typescript.ModuleKind.ESNext,
16
67
  })
17
68
 
18
- await Promise.all([
19
- // load workers
20
- (async () => {
21
- const [
22
- { default: EditorWorker },
23
- { default: JsonWorker },
24
- { default: CssWorker },
25
- { default: HtmlWorker },
26
- { default: TsWorker },
27
- ] = await Promise.all([
28
- import('monaco-editor/esm/vs/editor/editor.worker?worker'),
29
- import('monaco-editor/esm/vs/language/json/json.worker?worker'),
30
- import('monaco-editor/esm/vs/language/css/css.worker?worker'),
31
- import('monaco-editor/esm/vs/language/html/html.worker?worker'),
32
- import('monaco-editor/esm/vs/language/typescript/ts.worker?worker'),
33
- ])
34
-
35
- window.MonacoEnvironment = {
36
- getWorker(_: any, label: string) {
37
- if (label === 'json')
38
- return new JsonWorker()
39
- if (label === 'css' || label === 'scss' || label === 'less')
40
- return new CssWorker()
41
- if (label === 'html' || label === 'handlebars' || label === 'razor')
42
- return new HtmlWorker()
43
- if (label === 'typescript' || label === 'javascript')
44
- return new TsWorker()
45
- return new EditorWorker()
69
+ // Load types from server
70
+ import('#slidev/monaco-types')
71
+
72
+ const ata = configs.monacoTypesSource === 'cdn'
73
+ ? setupTypeAcquisition({
74
+ projectName: 'TypeScript Playground',
75
+ typescript: ts as any, // Version mismatch. No problem found so far.
76
+ logger: console,
77
+ delegate: {
78
+ receivedFile: (code: string, path: string) => {
79
+ defaults.addExtraLib(code, `file://${path}`)
80
+ const uri = monaco.Uri.file(path)
81
+ if (monaco.editor.getModel(uri) === null)
82
+ monaco.editor.createModel(code, 'javascript', uri)
46
83
  },
47
- }
48
- })(),
49
- ])
84
+ progress: (downloaded: number, total: number) => {
85
+ // eslint-disable-next-line no-console
86
+ console.debug(`[Typescript ATA] ${downloaded} / ${total}`)
87
+ },
88
+ },
89
+ })
90
+ : () => { }
91
+
92
+ monaco.languages.register({ id: 'vue' })
93
+ monaco.languages.register({ id: 'html' })
94
+ monaco.languages.register({ id: 'css' })
95
+ monaco.languages.register({ id: 'typescript' })
96
+ monaco.languages.register({ id: 'javascript' })
50
97
 
51
- // @ts-expect-error injected in runtime
52
- // eslint-disable-next-line unused-imports/no-unused-vars
53
- const injection_arg = monaco
54
- // eslint-disable-next-line prefer-const
55
- let injection_return: MonacoSetupReturn = {}
98
+ const { shiki, themes, shikiToMonaco } = await import('#slidev/shiki')
99
+ const highlighter = await shiki
56
100
 
57
- /* __async_injections__ */
101
+ const setupReturn: MonacoSetupReturn = {}
102
+ for (const setup of setups) {
103
+ const result = await setup(monaco)
104
+ Object.assign(setupReturn, result)
105
+ }
58
106
 
59
- if (getCurrentInstance())
60
- await new Promise<void>(resolve => onMounted(resolve))
107
+ // Use Shiki to highlight Monaco
108
+ shikiToMonaco(highlighter, monaco)
109
+ if (typeof themes === 'string') {
110
+ monaco.editor.setTheme(themes)
111
+ }
112
+ else {
113
+ watchEffect(() => {
114
+ monaco.editor.setTheme(isDark.value
115
+ ? themes.dark || 'vitesse-dark'
116
+ : themes.light || 'vitesse-light')
117
+ })
118
+ }
61
119
 
62
120
  return {
63
121
  monaco,
64
- ...injection_return,
122
+ ata,
123
+ ...setupReturn,
65
124
  }
66
125
  })
67
126
 
68
- export default setup
127
+ export async function addFile(raw: Promise<{ default: string }>, path: string) {
128
+ const code = (await raw).default
129
+ monaco.languages.typescript.typescriptDefaults.addExtraLib(code, `file:///${path}`)
130
+ monaco.editor.createModel(code, 'javascript', monaco.Uri.file(path))
131
+ }
69
132
 
70
- setup()
133
+ export default setup
package/setup/root.ts CHANGED
@@ -1,31 +1,75 @@
1
- /* __imports__ */
2
- import { watch } from 'vue'
1
+ import { computed, getCurrentInstance, reactive, ref, shallowRef, watch } from 'vue'
3
2
  import { useHead } from '@unhead/vue'
4
- import { nanoid } from 'nanoid'
3
+ import { useRouter } from 'vue-router'
5
4
  import { configs } from '../env'
6
5
  import { initSharedState, onPatch, patch } from '../state/shared'
7
6
  import { initDrawingState } from '../state/drawings'
8
- import { clicksContext, currentPage, getPath, isNotesViewer, isPresenter } from '../logic/nav'
9
- import { router } from '../routes'
10
- import { TRUST_ORIGINS } from '../constants'
11
- import { skipTransition } from '../composables/hmr'
7
+ import { TRUST_ORIGINS, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionSlidevContext } from '../constants'
8
+ import { skipTransition } from '../logic/hmr'
9
+ import { makeId } from '../logic/utils'
10
+ import { getSlidePath } from '../logic/slides'
11
+ import { createFixedClicks } from '../composables/useClicks'
12
+ import { isDark } from '../logic/dark'
13
+ import { useNav } from '../composables/useNav'
14
+ import setups from '#slidev/setups/root'
12
15
 
13
16
  export default function setupRoot() {
14
- // @ts-expect-error injected in runtime
15
- // eslint-disable-next-line unused-imports/no-unused-vars
16
- const injection_arg = undefined
17
+ const app = getCurrentInstance()!.appContext.app
17
18
 
18
- /* __injections__ */
19
+ const context = reactive({
20
+ nav: useNav(),
21
+ configs,
22
+ themeConfigs: computed(() => configs.themeConfig),
23
+ })
24
+ app.provide(injectionRenderContext, ref('none'))
25
+ app.provide(injectionSlidevContext, context)
26
+ app.provide(injectionCurrentPage, computed(() => context.nav.currentSlideNo))
27
+ app.provide(injectionClicksContext, shallowRef(createFixedClicks()))
28
+
29
+ // allows controls from postMessages
30
+ if (__DEV__) {
31
+ // @ts-expect-error expose global
32
+ window.__slidev__ = context
33
+ window.addEventListener('message', ({ data }) => {
34
+ if (data && data.target === 'slidev') {
35
+ if (data.type === 'navigate') {
36
+ context.nav.go(+data.no, +data.clicks || 0)
37
+ }
38
+ else if (data.type === 'css-vars') {
39
+ const root = document.documentElement
40
+ for (const [key, value] of Object.entries(data.vars || {}))
41
+ root.style.setProperty(key, value as any)
42
+ }
43
+ else if (data.type === 'color-schema') {
44
+ isDark.value = data.color === 'dark'
45
+ }
46
+ }
47
+ })
48
+ }
49
+
50
+ // User Setups
51
+ for (const setup of setups)
52
+ setup()
19
53
 
20
54
  const title = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
55
+
56
+ const {
57
+ clicksContext,
58
+ currentSlideNo,
59
+ hasPrimarySlide,
60
+ isNotesViewer,
61
+ isPresenter,
62
+ } = useNav()
63
+
21
64
  useHead({
22
65
  title,
23
66
  htmlAttrs: configs.htmlAttrs,
24
67
  })
68
+
25
69
  initSharedState(`${title} - shared`)
26
70
  initDrawingState(`${title} - drawings`)
27
71
 
28
- const id = `${location.origin}_${nanoid()}`
72
+ const id = `${location.origin}_${makeId()}`
29
73
 
30
74
  // update shared state
31
75
  function updateSharedState() {
@@ -37,11 +81,11 @@ export default function setupRoot() {
37
81
  return
38
82
 
39
83
  if (isPresenter.value) {
40
- patch('page', +currentPage.value)
84
+ patch('page', +currentSlideNo.value)
41
85
  patch('clicks', clicksContext.value.current)
42
86
  }
43
87
  else {
44
- patch('viewerPage', +currentPage.value)
88
+ patch('viewerPage', +currentSlideNo.value)
45
89
  patch('viewerClicks', clicksContext.value.current)
46
90
  }
47
91
 
@@ -51,17 +95,17 @@ export default function setupRoot() {
51
95
  time: new Date().getTime(),
52
96
  })
53
97
  }
98
+ const router = useRouter()
54
99
  router.afterEach(updateSharedState)
55
100
  watch(clicksContext, updateSharedState)
56
101
 
57
102
  onPatch((state) => {
58
- const routePath = router.currentRoute.value.path
59
- if (!routePath.match(/^\/(\d+|presenter)\/?/))
103
+ if (!hasPrimarySlide.value)
60
104
  return
61
- if (state.lastUpdate?.type === 'presenter' && (+state.page !== +currentPage.value || +clicksContext.value.current !== +state.clicks)) {
105
+ if (state.lastUpdate?.type === 'presenter' && (+state.page !== +currentSlideNo.value || +clicksContext.value.current !== +state.clicks)) {
62
106
  skipTransition.value = false
63
107
  router.replace({
64
- path: getPath(state.page),
108
+ path: getSlidePath(state.page, isPresenter.value),
65
109
  query: {
66
110
  ...router.currentRoute.value.query,
67
111
  clicks: state.clicks || 0,
@@ -1,18 +1,19 @@
1
- /* __imports__ */
2
1
  import { and, not, or } from '@vueuse/math'
3
2
  import type { NavOperations, ShortcutOptions } from '@slidev/types'
4
- import { downloadPDF, go, goFirst, goLast, next, nextSlide, prev, prevSlide } from '../logic/nav'
3
+ import { downloadPDF } from '../utils'
5
4
  import { toggleDark } from '../logic/dark'
6
5
  import { magicKeys, showGotoDialog, showOverview, toggleOverview } from '../state'
7
- import { drawingEnabled } from '../logic/drawings'
6
+ import { useNav } from '../composables/useNav'
7
+ import { useDrawings } from '../composables/useDrawings'
8
8
  import { currentOverviewPage, downOverviewPage, nextOverviewPage, prevOverviewPage, upOverviewPage } from './../logic/overview'
9
+ import setups from '#slidev/setups/shortcuts'
9
10
 
10
11
  export default function setupShortcuts() {
12
+ const { go, goFirst, goLast, next, nextSlide, prev, prevSlide } = useNav()
13
+ const { drawingEnabled } = useDrawings()
11
14
  const { escape, space, shift, left, right, up, down, enter, d, g, o, '`': backtick } = magicKeys
12
15
 
13
- // @ts-expect-error injected in runtime
14
- // eslint-disable-next-line unused-imports/no-unused-vars
15
- const injection_arg: NavOperations = {
16
+ const context: NavOperations = {
16
17
  next,
17
18
  prev,
18
19
  nextSlide,
@@ -28,8 +29,7 @@ export default function setupShortcuts() {
28
29
  showGotoDialog: () => showGotoDialog.value = !showGotoDialog.value,
29
30
  }
30
31
 
31
- // eslint-disable-next-line prefer-const
32
- let injection_return: ShortcutOptions[] = [
32
+ let shortcuts: ShortcutOptions[] = [
33
33
  { name: 'next_space', key: and(space, not(shift)), fn: next, autoRepeat: true },
34
34
  { name: 'prev_space', key: and(space, shift), fn: prev, autoRepeat: true },
35
35
  { name: 'next_right', key: and(right, not(shift), not(showOverview)), fn: next, autoRepeat: true },
@@ -58,11 +58,14 @@ export default function setupShortcuts() {
58
58
  },
59
59
  ]
60
60
 
61
- const baseShortcutNames = new Set(injection_return.map(s => s.name))
61
+ const baseShortcutNames = new Set(shortcuts.map(s => s.name))
62
62
 
63
- /* __chained_injections__ */
63
+ for (const setup of setups) {
64
+ const result = setup(context, shortcuts)
65
+ shortcuts = shortcuts.concat(result)
66
+ }
64
67
 
65
- const remainingBaseShortcutNames = injection_return.filter(s => s.name && baseShortcutNames.has(s.name))
68
+ const remainingBaseShortcutNames = shortcuts.filter(s => s.name && baseShortcutNames.has(s.name))
66
69
  if (remainingBaseShortcutNames.length === 0) {
67
70
  const message = [
68
71
  '========== WARNING ==========',
@@ -76,5 +79,5 @@ export default function setupShortcuts() {
76
79
  console.warn(message)
77
80
  }
78
81
 
79
- return injection_return
82
+ return shortcuts
80
83
  }
package/shim-vue.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ declare module 'vue' {
2
+ type SlideContext = import('./context').SlideContext
3
+ interface ComponentCustomProperties extends SlideContext {
4
+ }
5
+ }
6
+
7
+ declare module 'vue-router' {
8
+ import type { TransitionGroupProps } from 'vue'
9
+
10
+ interface RouteMeta {
11
+ // inherited from frontmatter
12
+ layout: string
13
+ name?: string
14
+ class?: string
15
+ clicks?: number
16
+ transition?: string | TransitionGroupProps | undefined
17
+ preload?: boolean
18
+
19
+ // slide info
20
+ slide?: Omit<import('@slidev/types').SlideInfo, 'source'> & {
21
+ noteHTML: string
22
+ filepath: string
23
+ start: number
24
+ id: number
25
+ no: number
26
+ }
27
+
28
+ // private fields
29
+ __clicksContext: import('@slidev/types').ClicksContext | undefined
30
+ __preloaded?: boolean
31
+ }
32
+ }
33
+
34
+ export {}
package/shim.d.ts CHANGED
@@ -1,23 +1,11 @@
1
- declare interface Window {
2
- // extend the window
3
- }
4
-
5
- declare module '*.vue';
6
-
7
- // with unplugin-vue-markdown, markdowns can be treat as Vue components
8
1
  declare module '*.md' {
2
+ // with unplugin-vue-markdown, markdowns can be treat as Vue components
9
3
  import type { ComponentOptions } from 'vue'
10
4
 
11
5
  const component: ComponentOptions
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