@slidev/client 0.49.0-beta.2 → 0.49.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.
@@ -17,7 +17,10 @@ import lz from 'lz-string'
17
17
  import type * as monaco from 'monaco-editor'
18
18
  import { computed, nextTick, onMounted, ref } from 'vue'
19
19
  import type { RawAtValue } from '@slidev/types'
20
+ import { whenever } from '@vueuse/core'
20
21
  import { makeId } from '../logic/utils'
22
+ import { useSlideContext } from '../context'
23
+ import { useNav } from '../composables/useNav'
21
24
  import CodeRunner from '../internals/CodeRunner.vue'
22
25
 
23
26
  const props = withDefaults(defineProps<{
@@ -76,6 +79,19 @@ const height = computed(() => {
76
79
  return props.height
77
80
  })
78
81
 
82
+ const loadTypes = ref<() => void>()
83
+ const { $page: thisSlideNo, $renderContext: renderContext } = useSlideContext()
84
+ const { currentSlideNo } = useNav()
85
+ const stopWatchTypesLoading = whenever(
86
+ () => Math.abs(thisSlideNo.value - currentSlideNo.value) <= 1 && loadTypes.value,
87
+ (loadTypes) => {
88
+ if (['slide', 'presenter'].includes(renderContext.value))
89
+ loadTypes()
90
+ else
91
+ setTimeout(loadTypes, 5000)
92
+ },
93
+ )
94
+
79
95
  onMounted(async () => {
80
96
  // Lazy load monaco, so it will be bundled in async chunk
81
97
  const { default: setup } = await import('../setup/monaco')
@@ -137,11 +153,15 @@ onMounted(async () => {
137
153
  })
138
154
  editableEditor = editor
139
155
  }
140
- if (props.ata) {
141
- ata(editableEditor.getValue())
142
- editableEditor.onDidChangeModelContent(debounce(1000, () => {
156
+ loadTypes.value = () => {
157
+ stopWatchTypesLoading()
158
+ import('#slidev/monaco-types')
159
+ if (props.ata) {
143
160
  ata(editableEditor.getValue())
144
- }))
161
+ editableEditor.onDidChangeModelContent(debounce(1000, () => {
162
+ ata(editableEditor.getValue())
163
+ }))
164
+ }
145
165
  }
146
166
  const originalLayoutContentWidget = editableEditor.layoutContentWidget.bind(editableEditor)
147
167
  editableEditor.layoutContentWidget = (widget: any) => {
package/constants.ts CHANGED
@@ -61,6 +61,9 @@ export const HEADMATTER_FIELDS = [
61
61
  'highlighter',
62
62
  'lineNumbers',
63
63
  'monaco',
64
+ 'monacoTypesSource',
65
+ 'monacoTypesAdditionalPackages',
66
+ 'monacoRunAdditionalDeps',
64
67
  'remoteAssets',
65
68
  'selectable',
66
69
  'record',
@@ -102,6 +102,11 @@ watchEffect(() => {
102
102
  // Watch rowCount, make sure up and down shortcut work correctly.
103
103
  overviewRowCount.value = rowCount.value
104
104
  })
105
+
106
+ const activeSlidesLoaded = ref(false)
107
+ setTimeout(() => {
108
+ activeSlidesLoaded.value = true
109
+ }, 3000)
105
110
  </script>
106
111
 
107
112
  <template>
@@ -112,6 +117,7 @@ watchEffect(() => {
112
117
  leave-to-class="opacity-0 scale-102 !backdrop-blur-0px"
113
118
  >
114
119
  <div
120
+ v-if="value || activeSlidesLoaded"
115
121
  v-show="value"
116
122
  class="bg-main !bg-opacity-75 p-16 py-20 overflow-y-auto backdrop-blur-5px fixed left-0 right-0 top-0 h-[calc(var(--vh,1vh)*100)]"
117
123
  @click="close()"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@slidev/client",
3
3
  "type": "module",
4
- "version": "0.49.0-beta.2",
4
+ "version": "0.49.0-beta.4",
5
5
  "description": "Presentation slides for developers",
6
6
  "author": "antfu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "@slidev/rough-notation": "^0.1.0",
38
38
  "@typescript/ata": "^0.9.4",
39
39
  "@unhead/vue": "^1.9.5",
40
- "@unocss/reset": "^0.59.2",
40
+ "@unocss/reset": "^0.59.3",
41
41
  "@vueuse/core": "^10.9.0",
42
42
  "@vueuse/math": "^10.9.0",
43
43
  "@vueuse/motion": "^2.1.0",
@@ -53,17 +53,17 @@
53
53
  "prettier": "^3.2.5",
54
54
  "recordrtc": "^5.6.2",
55
55
  "shiki": "^1.3.0",
56
- "shiki-magic-move": "^0.3.5",
56
+ "shiki-magic-move": "^0.3.6",
57
57
  "typescript": "^5.4.5",
58
- "unocss": "^0.59.2",
59
- "vue": "^3.4.21",
58
+ "unocss": "^0.59.3",
59
+ "vue": "^3.4.22",
60
60
  "vue-demi": "^0.14.7",
61
61
  "vue-router": "^4.3.0",
62
62
  "yaml": "^2.4.1",
63
- "@slidev/parser": "0.49.0-beta.2",
64
- "@slidev/types": "0.49.0-beta.2"
63
+ "@slidev/parser": "0.49.0-beta.4",
64
+ "@slidev/types": "0.49.0-beta.4"
65
65
  },
66
66
  "devDependencies": {
67
- "vite": "^5.2.8"
67
+ "vite": "^5.2.9"
68
68
  }
69
69
  }
@@ -1,8 +1,9 @@
1
- import { createSingletonPromise, ensurePrefix, slash } from '@antfu/utils'
2
- import type { CodeRunner, CodeRunnerContext, CodeRunnerOutput, CodeRunnerOutputText, CodeRunnerOutputs } from '@slidev/types'
1
+ import { createSingletonPromise } from '@antfu/utils'
2
+ import type { CodeRunner, CodeRunnerOutput, CodeRunnerOutputText, CodeRunnerOutputs } from '@slidev/types'
3
3
  import type { CodeToHastOptions } from 'shiki'
4
4
  import type ts from 'typescript'
5
5
  import { isDark } from '../logic/dark'
6
+ import deps from '#slidev/monaco-run-deps'
6
7
  import setups from '#slidev/setups/code-runners'
7
8
 
8
9
  export default createSingletonPromise(async () => {
@@ -25,18 +26,6 @@ export default createSingletonPromise(async () => {
25
26
  ...options,
26
27
  })
27
28
 
28
- const resolveId = async (specifier: string) => {
29
- if (!'./'.includes(specifier[0]) && !/^(@[^\/:]+?\/)?[^\/:]+$/.test(specifier))
30
- return specifier // this might be a url or something else
31
- const res = await fetch(`/@slidev/resolve-id?specifier=${specifier}`)
32
- if (!res.ok)
33
- return null
34
- const id = await res.text()
35
- if (!id)
36
- return null
37
- return `/@fs${ensurePrefix('/', slash(id))}`
38
- }
39
-
40
29
  const run = async (code: string, lang: string, options: Record<string, unknown>): Promise<CodeRunnerOutputs> => {
41
30
  try {
42
31
  const runner = runners[lang]
@@ -47,7 +36,6 @@ export default createSingletonPromise(async () => {
47
36
  {
48
37
  options,
49
38
  highlight,
50
- resolveId,
51
39
  run: async (code, lang) => {
52
40
  return await run(code, lang, options)
53
41
  },
@@ -85,16 +73,20 @@ async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
85
73
  replace.clear = () => allLogs.length = 0
86
74
  const vmConsole = Object.assign({}, console, replace)
87
75
  try {
88
- const safeJS = `return async (console) => {
89
- window.console = console
76
+ const safeJS = `return async (console, __slidev_import) => {
90
77
  ${sanitizeJS(code)}
91
78
  }`
92
79
  // eslint-disable-next-line no-new-func
93
- await (new Function(safeJS)())(vmConsole)
80
+ await (new Function(safeJS)())(vmConsole, (specifier: string) => {
81
+ const mod = deps[specifier]
82
+ if (!mod)
83
+ throw new Error(`Module not found: ${specifier}.\nAvailable modules: ${Object.keys(deps).join(', ')}. Please refer to https://sli.dev/custom/config-code-runners#additional-runner-dependencies`)
84
+ return mod
85
+ })
94
86
  }
95
87
  catch (error) {
96
88
  return {
97
- error: `ERROR: ${error}`,
89
+ error: String(error),
98
90
  }
99
91
  }
100
92
 
@@ -175,7 +167,7 @@ async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
175
167
 
176
168
  let tsModule: typeof import('typescript') | undefined
177
169
 
178
- export async function runTypeScript(code: string, context: CodeRunnerContext) {
170
+ export async function runTypeScript(code: string) {
179
171
  tsModule ??= await import('typescript')
180
172
 
181
173
  code = tsModule.transpileModule(code, {
@@ -188,11 +180,8 @@ export async function runTypeScript(code: string, context: CodeRunnerContext) {
188
180
  },
189
181
  }).outputText
190
182
 
191
- const importRegex = /import\s*\(\s*(['"])(.+?)['"]\s*\)/g
192
- const idMap: Record<string, string> = {}
193
- for (const [,,specifier] of code.matchAll(importRegex)!)
194
- idMap[specifier] = await context.resolveId(specifier) ?? specifier
195
- code = code.replace(importRegex, (_full, quote, specifier) => `import(${quote}${idMap[specifier] ?? specifier}${quote})`)
183
+ const importRegex = /import\s*\((.+)\)/g
184
+ code = code.replace(importRegex, (_full, specifier) => `__slidev_import(${specifier})`)
196
185
 
197
186
  return await runJavaScript(code)
198
187
  }
package/setup/main.ts CHANGED
@@ -9,7 +9,7 @@ import { createVClickDirectives } from '../modules/v-click'
9
9
  import { createVMarkDirective } from '../modules/v-mark'
10
10
  import { createVDragDirective } from '../modules/v-drag'
11
11
  import { createVMotionDirectives } from '../modules/v-motion'
12
- import { routes } from '../routes'
12
+ import setupRoutes from '../setup/routes'
13
13
  import setups from '#slidev/setups/main'
14
14
 
15
15
  import '#slidev/styles'
@@ -28,7 +28,7 @@ export default async function setupMain(app: App) {
28
28
  history: __SLIDEV_HASH_ROUTE__
29
29
  ? createWebHashHistory(import.meta.env.BASE_URL)
30
30
  : createWebHistory(import.meta.env.BASE_URL),
31
- routes,
31
+ routes: setupRoutes(),
32
32
  })
33
33
 
34
34
  app.use(router)
package/setup/monaco.ts CHANGED
@@ -66,9 +66,6 @@ const setup = createSingletonPromise(async () => {
66
66
  module: monaco.languages.typescript.ModuleKind.ESNext,
67
67
  })
68
68
 
69
- // Load types from server
70
- import('#slidev/monaco-types')
71
-
72
69
  const ata = configs.monacoTypesSource === 'cdn'
73
70
  ? setupTypeAcquisition({
74
71
  projectName: 'TypeScript Playground',
@@ -0,0 +1,80 @@
1
+ import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
2
+ import configs from '#slidev/configs'
3
+ import setups from '#slidev/setups/routes'
4
+
5
+ export default function setupRoutes() {
6
+ const routes: RouteRecordRaw[] = []
7
+
8
+ if (__SLIDEV_FEATURE_PRESENTER__) {
9
+ function passwordGuard(to: RouteLocationNormalized) {
10
+ if (!configs.remote || configs.remote === to.query.password)
11
+ return true
12
+ if (configs.remote && to.query.password === undefined) {
13
+ // eslint-disable-next-line no-alert
14
+ const password = prompt('Enter password')
15
+ if (configs.remote === password)
16
+ return true
17
+ }
18
+ if (to.params.no)
19
+ return { path: `/${to.params.no}` }
20
+ return { path: '' }
21
+ }
22
+
23
+ routes.push(
24
+ {
25
+ name: 'entry',
26
+ path: '/entry',
27
+ component: () => import('../pages/entry.vue'),
28
+ },
29
+ {
30
+ name: 'overview',
31
+ path: '/overview',
32
+ component: () => import('../pages/overview.vue'),
33
+ },
34
+ {
35
+ name: 'notes',
36
+ path: '/notes',
37
+ component: () => import('../pages/notes.vue'),
38
+ beforeEnter: passwordGuard,
39
+ },
40
+ {
41
+ name: 'presenter',
42
+ path: '/presenter/:no',
43
+ component: () => import('../pages/presenter.vue'),
44
+ beforeEnter: passwordGuard,
45
+ },
46
+ {
47
+ path: '/presenter',
48
+ redirect: { path: '/presenter/1' },
49
+ },
50
+ )
51
+ }
52
+
53
+ if (__SLIDEV_HAS_SERVER__) {
54
+ routes.push(
55
+ {
56
+ name: 'print',
57
+ path: '/print',
58
+ component: () => import('../pages/print.vue'),
59
+ },
60
+ {
61
+ path: '/presenter/print',
62
+ component: () => import('../pages/presenter/print.vue'),
63
+ },
64
+ )
65
+ }
66
+
67
+ routes.push(
68
+ {
69
+ name: 'play',
70
+ path: '/:no',
71
+ component: () => import('../pages/play.vue'),
72
+ },
73
+ {
74
+ path: '',
75
+ redirect: { path: '/1' },
76
+ },
77
+ )
78
+
79
+ return setups.reduce((routes, setup) => setup(routes), routes)
80
+ }
package/routes.ts DELETED
@@ -1,68 +0,0 @@
1
- import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
2
- import configs from '#slidev/configs'
3
-
4
- export const routes: RouteRecordRaw[] = [
5
- {
6
- name: 'print',
7
- path: '/print',
8
- component: () => import('./pages/print.vue'),
9
- },
10
-
11
- // Redirects
12
- { path: '', redirect: { path: '/1' } },
13
- ]
14
-
15
- if (__SLIDEV_FEATURE_PRESENTER__) {
16
- function passwordGuard(to: RouteLocationNormalized) {
17
- if (!configs.remote || configs.remote === to.query.password)
18
- return true
19
- if (configs.remote && to.query.password === undefined) {
20
- // eslint-disable-next-line no-alert
21
- const password = prompt('Enter password')
22
- if (configs.remote === password)
23
- return true
24
- }
25
- if (to.params.no)
26
- return { path: `/${to.params.no}` }
27
- return { path: '' }
28
- }
29
-
30
- routes.push({
31
- path: '/presenter/print',
32
- component: () => import('./pages/presenter/print.vue'),
33
- })
34
- if (__SLIDEV_HAS_SERVER__) {
35
- routes.push({
36
- name: 'entry',
37
- path: '/entry',
38
- component: () => import('./pages/entry.vue'),
39
- })
40
- routes.push({
41
- name: 'overview',
42
- path: '/overview',
43
- component: () => import('./pages/overview.vue'),
44
- })
45
- routes.push({
46
- name: 'notes',
47
- path: '/notes',
48
- component: () => import('./pages/notes.vue'),
49
- beforeEnter: passwordGuard,
50
- })
51
- }
52
- routes.push({
53
- name: 'presenter',
54
- path: '/presenter/:no',
55
- component: () => import('./pages/presenter.vue'),
56
- beforeEnter: passwordGuard,
57
- })
58
- routes.push({
59
- path: '/presenter',
60
- redirect: { path: '/presenter/1' },
61
- })
62
- }
63
-
64
- routes.push({
65
- name: 'play',
66
- path: '/:no',
67
- component: () => import('./pages/play.vue'),
68
- })