@slidev/client 0.48.0-beta.15 → 0.48.0-beta.16

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.
@@ -12,11 +12,11 @@ Learn more: https://sli.dev/guide/syntax.html#monaco-editor
12
12
  -->
13
13
 
14
14
  <script setup lang="ts">
15
- import { computed, onMounted, ref } from 'vue'
16
- import { useEventListener } from '@vueuse/core'
17
- import type * as monaco from 'monaco-editor'
15
+ import * as monaco from 'monaco-editor'
16
+ import { computed, nextTick, onMounted, ref } from 'vue'
17
+ import { debounce } from '@antfu/utils'
18
18
  import { decompressFromBase64 } from 'lz-string'
19
- import { isDark } from '../logic/dark'
19
+ import setup from '../setup/monaco'
20
20
  import { makeId } from '../logic/utils'
21
21
 
22
22
  const props = withDefaults(defineProps<{
@@ -25,109 +25,125 @@ const props = withDefaults(defineProps<{
25
25
  lang?: string
26
26
  readonly?: boolean
27
27
  lineNumbers?: 'on' | 'off' | 'relative' | 'interval'
28
- height?: number | string
28
+ height?: number | string // Posible values: 'initial', 'auto', '100%', '200px', etc.
29
29
  editorOptions?: monaco.editor.IEditorOptions
30
+ ata?: boolean
30
31
  }>(), {
31
32
  codeLz: '',
32
33
  lang: 'typescript',
33
34
  readonly: false,
34
35
  lineNumbers: 'off',
35
- height: 'auto',
36
+ height: 'initial',
37
+ ata: true,
36
38
  })
37
39
 
38
- const id = makeId()
39
- const code = ref(decompressFromBase64(props.codeLz))
40
- const diff = ref(props.diffLz ? decompressFromBase64(props.diffLz) : null)
41
- const lineHeight = +(getComputedStyle(document.body).getPropertyValue('--slidev-code-line-height') || '18').replace('px', '') || 18
42
- const editorHeight = ref(0)
43
- const calculatedHeight = computed(() => code.value.split(/\r?\n/g).length * lineHeight)
44
- const height = computed(() => {
45
- return props.height === 'auto' ? `${Math.max(calculatedHeight.value, editorHeight.value) + 20}px` : props.height
46
- })
47
-
48
- const iframe = ref<HTMLIFrameElement>()
40
+ const code = decompressFromBase64(props.codeLz).trimEnd()
41
+ const diff = props.diffLz && decompressFromBase64(props.diffLz).trimEnd()
49
42
 
50
- const cssVars = [
51
- '--slidev-code-font-size',
52
- '--slidev-code-font-family',
53
- '--slidev-code-background',
54
- '--slidev-code-line-height',
55
- '--slidev-code-padding',
56
- '--slidev-code-margin',
57
- '--slidev-code-radius',
58
- ]
59
-
60
- function getStyleObject(el: Element) {
61
- const object: Record<string, string> = {}
62
- const style = getComputedStyle(el)
63
- for (const v of cssVars)
64
- object[v] = style.getPropertyValue(v)
65
- return object
43
+ const langMap: Record<string, string> = {
44
+ ts: 'typescript',
45
+ js: 'javascript',
66
46
  }
47
+ const lang = langMap[props.lang] ?? props.lang
48
+ const extMap: Record<string, string> = {
49
+ typescript: 'mts',
50
+ javascript: 'mjs',
51
+ ts: 'mts',
52
+ js: 'mjs',
53
+ }
54
+ const ext = extMap[props.lang] ?? props.lang
67
55
 
68
- onMounted(() => {
69
- const frame = iframe.value!
70
- frame.setAttribute('sandbox', [
71
- 'allow-forms',
72
- 'allow-modals',
73
- 'allow-pointer-lock',
74
- 'allow-popups',
75
- 'allow-same-origin',
76
- 'allow-scripts',
77
- 'allow-top-navigation-by-user-activation',
78
- ].join(' '))
79
-
80
- let src = __DEV__
81
- ? `${location.origin}${__SLIDEV_CLIENT_ROOT__}/`
82
- : import.meta.env.BASE_URL
83
- src += `iframes/monaco/index.html?id=${id}&lineNumbers=${props.lineNumbers}&lang=${props.lang}`
84
- if (diff.value)
85
- src += '&diff=1'
86
- frame.src = src
56
+ const outer = ref<HTMLDivElement>()
57
+ const container = ref<HTMLDivElement>()
87
58
 
88
- frame.style.backgroundColor = 'transparent'
59
+ const contentHeight = ref(0)
60
+ const initialHeight = ref<number>()
61
+ const height = computed(() => {
62
+ if (props.height === 'auto')
63
+ return `${contentHeight.value}px`
64
+ if (props.height === 'initial')
65
+ return `${initialHeight.value}px`
66
+ return props.height
89
67
  })
90
68
 
91
- function post(payload: any) {
92
- iframe.value?.contentWindow?.postMessage(
93
- JSON.stringify({
94
- type: 'slidev-monaco',
95
- data: payload,
96
- id,
97
- }),
98
- location.origin,
99
- )
100
- }
69
+ onMounted(async () => {
70
+ const { ata } = await setup()
71
+ const model = monaco.editor.createModel(code, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
72
+ const commonOptions = {
73
+ automaticLayout: true,
74
+ readOnly: props.readonly,
75
+ lineNumbers: props.lineNumbers,
76
+ minimap: { enabled: false },
77
+ overviewRulerBorder: false,
78
+ overviewRulerLanes: 0,
79
+ padding: { top: 10, bottom: 10 },
80
+ lineNumbersMinChars: 3,
81
+ bracketPairColorization: { enabled: false },
82
+ tabSize: 2,
83
+ fontSize: 11.5,
84
+ fontFamily: 'var(--slidev-code-font-family)',
85
+ scrollBeyondLastLine: false,
86
+ ...props.editorOptions,
87
+ } satisfies monaco.editor.IStandaloneEditorConstructionOptions & monaco.editor.IDiffEditorConstructionOptions
101
88
 
102
- useEventListener(window, 'message', ({ data: payload }) => {
103
- if (payload.id !== id)
104
- return
105
- if (payload.type === 'slidev-monaco-loaded') {
106
- if (iframe.value) {
107
- post({
108
- code: code.value,
109
- diff: diff.value,
110
- lang: props.lang,
111
- readonly: props.readonly,
112
- lineNumbers: props.lineNumbers,
113
- editorOptions: props.editorOptions,
114
- dark: isDark.value,
115
- style: Object.entries(getStyleObject(iframe.value)).map(([k, v]) => `${k}: ${v};`).join(''),
116
- })
89
+ let editableEditor: monaco.editor.IStandaloneCodeEditor
90
+ if (diff) {
91
+ const diffModel = monaco.editor.createModel(diff, lang, monaco.Uri.parse(`file:///${nanoid()}.${ext}`))
92
+ const editor = monaco.editor.createDiffEditor(container.value!, {
93
+ renderOverviewRuler: false,
94
+ ...commonOptions,
95
+ })
96
+ editor.setModel({
97
+ original: model,
98
+ modified: diffModel,
99
+ })
100
+ const originalEditor = editor.getOriginalEditor()
101
+ const modifiedEditor = editor.getModifiedEditor()
102
+ const onContentSizeChange = () => {
103
+ const newHeight = Math.max(originalEditor.getContentHeight(), modifiedEditor.getContentHeight()) + 4
104
+ initialHeight.value ??= newHeight
105
+ contentHeight.value = newHeight
106
+ nextTick(() => editor.layout())
107
+ }
108
+ originalEditor.onDidContentSizeChange(onContentSizeChange)
109
+ modifiedEditor.onDidContentSizeChange(onContentSizeChange)
110
+ editableEditor = modifiedEditor
111
+ }
112
+ else {
113
+ const editor = monaco.editor.create(container.value!, {
114
+ model,
115
+ lineDecorationsWidth: 0,
116
+ ...commonOptions,
117
+ })
118
+ editor.onDidContentSizeChange((e) => {
119
+ const newHeight = e.contentHeight + 4
120
+ initialHeight.value ??= newHeight
121
+ contentHeight.value = newHeight
122
+ nextTick(() => editableEditor.layout())
123
+ })
124
+ editableEditor = editor
125
+ }
126
+ if (props.ata) {
127
+ ata(editableEditor.getValue())
128
+ editableEditor.onDidChangeModelContent(debounce(1000, () => {
129
+ ata(editableEditor.getValue())
130
+ }))
131
+ }
132
+ const originalLayoutContentWidget = editableEditor.layoutContentWidget.bind(editableEditor)
133
+ editableEditor.layoutContentWidget = (widget: any) => {
134
+ originalLayoutContentWidget(widget)
135
+ const id = widget.getId()
136
+ if (id === 'editor.contrib.resizableContentHoverWidget') {
137
+ widget._resizableNode.domNode.style.transform = widget._positionPreference === 1
138
+ ? /* ABOVE */ `translateY(calc(100% * (var(--slidev-slide-scale) - 1)))`
139
+ : /* BELOW */ `` // reset
117
140
  }
118
- return
119
141
  }
120
- if (payload.type !== 'slidev-monaco')
121
- return
122
- if (payload.data?.height)
123
- editorHeight.value = payload.data?.height
124
- if (payload?.data?.code && code.value !== payload.data.code)
125
- code.value = payload.data.code
126
- if (payload?.data?.diff && diff.value !== payload.data.diff)
127
- diff.value = payload.data.diff
128
142
  })
129
143
  </script>
130
144
 
131
145
  <template>
132
- <iframe ref="iframe" class="text-base w-full rounded" :style="{ height }" />
146
+ <div ref="outer" class="slidev-monaco-container" :style="{ height }">
147
+ <div ref="container" class="absolute inset-0.5" />
148
+ </div>
133
149
  </template>
package/logic/nav.ts CHANGED
@@ -135,16 +135,13 @@ export function go(page: number | string, clicks?: number) {
135
135
  export function useSwipeControls(root: Ref<HTMLElement | undefined>) {
136
136
  const swipeBegin = ref(0)
137
137
  const { direction, distanceX, distanceY } = usePointerSwipe(root, {
138
- onSwipeStart(e) {
139
- if (e.pointerType !== 'touch')
140
- return
138
+ pointerTypes: ['touch'],
139
+ onSwipeStart() {
141
140
  if (isDrawing.value)
142
141
  return
143
142
  swipeBegin.value = timestamp()
144
143
  },
145
- onSwipeEnd(e) {
146
- if (e.pointerType !== 'touch')
147
- return
144
+ onSwipeEnd() {
148
145
  if (!swipeBegin.value)
149
146
  return
150
147
  if (isDrawing.value)
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.15",
4
+ "version": "0.48.0-beta.16",
5
5
  "description": "Presentation slides for developers",
6
6
  "author": "antfu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -29,8 +29,10 @@
29
29
  "@antfu/utils": "^0.7.7",
30
30
  "@iconify-json/carbon": "^1.1.30",
31
31
  "@iconify-json/ph": "^1.1.11",
32
+ "@shikijs/monaco": "^1.1.7",
32
33
  "@shikijs/vitepress-twoslash": "^1.1.7",
33
34
  "@slidev/rough-notation": "^0.1.0",
35
+ "@typescript/ata": "^0.9.4",
34
36
  "@unhead/vue": "^1.8.10",
35
37
  "@unocss/reset": "^0.58.5",
36
38
  "@vueuse/core": "^10.8.0",
@@ -46,16 +48,18 @@
46
48
  "katex": "^0.16.9",
47
49
  "lz-string": "^1.5.0",
48
50
  "mermaid": "^10.8.0",
49
- "monaco-editor": "^0.37.1",
51
+ "monaco-editor": "^0.46.0",
52
+ "nanoid": "^5.0.6",
50
53
  "prettier": "^3.2.5",
51
54
  "recordrtc": "^5.6.2",
52
55
  "resolve": "^1.22.8",
56
+ "shiki": "^1.1.7",
53
57
  "shiki-magic-move": "^0.1.0",
54
58
  "unocss": "^0.58.5",
55
59
  "vue": "^3.4.20",
56
60
  "vue-router": "^4.3.0",
57
- "@slidev/types": "0.48.0-beta.15",
58
- "@slidev/parser": "0.48.0-beta.15"
61
+ "@slidev/parser": "0.48.0-beta.16",
62
+ "@slidev/types": "0.48.0-beta.16"
59
63
  },
60
64
  "devDependencies": {
61
65
  "vite": "^5.1.4"
package/setup/monaco.ts CHANGED
@@ -1,52 +1,115 @@
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'
19
+
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'
5
25
 
6
26
  /* __imports__ */
7
27
 
28
+ window.MonacoEnvironment = {
29
+ getWorker(_, label) {
30
+ if (label === 'json')
31
+ return new JsonWorker()
32
+ if (label === 'css' || label === 'scss' || label === 'less')
33
+ return new CssWorker()
34
+ if (label === 'html' || label === 'handlebars' || label === 'razor')
35
+ return new HtmlWorker()
36
+ if (label === 'typescript' || label === 'javascript')
37
+ return new TsWorker()
38
+ return new EditorWorker()
39
+ },
40
+ }
41
+
42
+ class ContextViewService2 extends ContextViewService {
43
+ showContextView(...args: any) {
44
+ super.showContextView(...args)
45
+ // @ts-expect-error missing types
46
+ const contextView = this.contextView.view as HTMLElement
47
+ contextView.style.left = `calc(${contextView.style.left} / var(--slidev-slide-scale))`
48
+ contextView.style.top = `calc(${contextView.style.top} / var(--slidev-slide-scale))`
49
+ // Reset the scale to 1. Otherwise, the sub-menu will be in the wrong position.
50
+ contextView.style.transform = `scale(calc(1 / var(--slidev-slide-scale)))`
51
+ contextView.style.transformOrigin = '0 0'
52
+ }
53
+ }
54
+
8
55
  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,
56
+ // Initialize services first, otherwise we can't override them.
57
+ StandaloneServices.initialize({
58
+ contextViewService: new SyncDescriptor(ContextViewService2, [], true),
59
+ })
60
+
61
+ const defaults = monaco.languages.typescript.typescriptDefaults
62
+
63
+ defaults.setCompilerOptions({
64
+ ...defaults.getCompilerOptions(),
15
65
  strict: true,
66
+ moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
67
+ module: monaco.languages.typescript.ModuleKind.ESNext,
16
68
  })
17
69
 
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()
70
+ // Load types from server
71
+ import('#slidev/monaco-types')
72
+
73
+ const ata = configs.monacoTypesSource === 'cdn'
74
+ ? setupTypeAcquisition({
75
+ projectName: 'TypeScript Playground',
76
+ typescript: ts as any, // Version mismatch. No problem found so far.
77
+ logger: console,
78
+ delegate: {
79
+ receivedFile: (code: string, path: string) => {
80
+ defaults.addExtraLib(code, `file://${path}`)
81
+ const uri = monaco.Uri.file(path)
82
+ if (monaco.editor.getModel(uri) === null)
83
+ monaco.editor.createModel(code, 'javascript', uri)
84
+ },
85
+ progress: (downloaded: number, total: number) => {
86
+ // eslint-disable-next-line no-console
87
+ console.debug(`[Typescript ATA] ${downloaded} / ${total}`)
46
88
  },
47
- }
48
- })(),
49
- ])
89
+ },
90
+ })
91
+ : () => { }
92
+
93
+ // monaco.languages.register({ id: 'vue' })
94
+ monaco.languages.register({ id: 'typescript' })
95
+ monaco.languages.register({ id: 'javascript' })
96
+
97
+ const { shiki, themes, shikiToMonaco } = await import('#slidev/shiki')
98
+ const highlighter = await shiki
99
+
100
+ // Use Shiki to highlight Monaco
101
+ shikiToMonaco(highlighter, monaco)
102
+
103
+ if (typeof themes === 'string') {
104
+ monaco.editor.setTheme(themes)
105
+ }
106
+ else {
107
+ watchEffect(() => {
108
+ monaco.editor.setTheme(isDark.value
109
+ ? themes.dark || 'vitesse-dark'
110
+ : themes.light || 'vitesse-light')
111
+ })
112
+ }
50
113
 
51
114
  // @ts-expect-error injected in runtime
52
115
  // eslint-disable-next-line unused-imports/no-unused-vars
@@ -56,15 +119,11 @@ const setup = createSingletonPromise(async () => {
56
119
 
57
120
  /* __async_injections__ */
58
121
 
59
- if (getCurrentInstance())
60
- await new Promise<void>(resolve => onMounted(resolve))
61
-
62
122
  return {
63
123
  monaco,
124
+ ata,
64
125
  ...injection_return,
65
126
  }
66
127
  })
67
128
 
68
129
  export default setup
69
-
70
- setup()
package/shim.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- // with unplugin-vue-markdown, markdowns can be treat as Vue components
2
1
  declare module '*.md' {
2
+ // with unplugin-vue-markdown, markdowns can be treat as Vue components
3
3
  import type { ComponentOptions } from 'vue'
4
4
 
5
5
  const component: ComponentOptions
@@ -0,0 +1,27 @@
1
+ div[widgetid='messageoverlay'] {
2
+ transform: translateY(calc(100% * (var(--slidev-slide-scale) - 1)));
3
+ }
4
+
5
+ .slidev-monaco-container {
6
+ position: relative;
7
+ margin: var(--slidev-code-margin);
8
+ padding: var(--slidev-code-padding);
9
+ line-height: var(--slidev-code-line-height);
10
+ border-radius: var(--slidev-code-radius);
11
+ background: var(--slidev-code-background);
12
+ }
13
+
14
+ .slidev-monaco-container .monaco-editor {
15
+ --monaco-monospace-font: var(--slidev-code-font-family);
16
+ --vscode-editor-background: var(--slidev-code-background);
17
+ --vscode-editorGutter-background: var(--slidev-code-background);
18
+ }
19
+
20
+ /** Revert styles */
21
+ .slidev-monaco-container .monaco-editor a {
22
+ border-bottom: none;
23
+ }
24
+
25
+ .slidev-monaco-container .monaco-editor a:hover {
26
+ border-bottom: none;
27
+ }
@@ -1,28 +0,0 @@
1
- html,
2
- body,
3
- #container {
4
- padding: 0;
5
- margin: 0;
6
- background: var(--slidev-code-background);
7
- width: 100%;
8
- height: 200%;
9
- }
10
-
11
- #container {
12
- padding: var(--slidev-code-padding);
13
- margin: var(--slidev-code-margin);
14
- border-radius: var(--slidev-code-radius);
15
- }
16
-
17
- .monaco-editor .monaco-hover {
18
- border-radius: var(--slidev-code-radius);
19
- overflow: hidden;
20
- border: none;
21
- outline: none;
22
- }
23
-
24
- .monaco-editor .lines-content,
25
- .monaco-editor .view-line,
26
- .monaco-editor .view-lines {
27
- user-select: none;
28
- }
@@ -1,7 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <body>
4
- <div id="container"></div>
5
- <script type="module" src="./index.ts"></script>
6
- </body>
7
- </html>
@@ -1,260 +0,0 @@
1
- import '/@slidev/styles'
2
- import './index.css'
3
-
4
- import type * as monaco from 'monaco-editor'
5
- import { formatCode } from '../../setup/prettier'
6
- import setupMonaco from '../../setup/monaco'
7
- import '/@slidev/monaco-types'
8
-
9
- const url = new URL(location.href)
10
- const props = {
11
- id: url.searchParams.get('id'),
12
- code: '',
13
- diff: '',
14
- lang: url.searchParams.get('lang') ?? 'typescript',
15
- readonly: false,
16
- lineNumbers: url.searchParams.get('lineNumbers') ?? 'off',
17
- dark: false,
18
- style: '',
19
- editorOptions: {},
20
- }
21
-
22
- const styleObject = document.createElement('style')
23
- let originalEditor: monaco.editor.IStandaloneCodeEditor
24
- let modifiedEditor: monaco.editor.IStandaloneCodeEditor
25
- let format: () => void = () => { }
26
- let update: () => void = () => { }
27
-
28
- document.body.appendChild(styleObject)
29
-
30
- function lang() {
31
- switch (props.lang) {
32
- case 'ts':
33
- case 'tsx':
34
- return 'typescript'
35
- case 'jsx':
36
- case 'js':
37
- return 'javascript'
38
- default:
39
- return props.lang
40
- }
41
- }
42
-
43
- function ext() {
44
- switch (lang()) {
45
- case 'typescript':
46
- return 'ts'
47
- case 'javascript':
48
- return 'js'
49
- default:
50
- return lang()
51
- }
52
- }
53
-
54
- function post(data: any, type = 'slidev-monaco') {
55
- if (window.parent === window)
56
- return
57
-
58
- window.parent.postMessage(
59
- {
60
- type,
61
- id: props.id,
62
- data,
63
- },
64
- location.origin,
65
- )
66
- }
67
-
68
- async function start() {
69
- const { monaco, theme = {}, editorOptions = {} } = await setupMonaco()
70
-
71
- const style = getComputedStyle(document.documentElement)
72
- const container = document.getElementById('container')!
73
-
74
- const model = monaco.editor.createModel(
75
- props.code,
76
- lang(),
77
- monaco.Uri.parse(`file:///root/${Date.now()}.${ext()}`),
78
- )
79
-
80
- if (url.searchParams.get('diff')) {
81
- // Diff editor
82
- const diffModel = monaco.editor.createModel(
83
- props.diff,
84
- lang(),
85
- monaco.Uri.parse(`file:///root/${Date.now()}.${ext()}`),
86
- )
87
- const monacoEditor = monaco.editor.createDiffEditor(container, {
88
- fontSize: +style.getPropertyValue('--slidev-code-font-size').replace(/px/g, ''),
89
- fontFamily: style.getPropertyValue('--slidev-code-font-family'),
90
- lineHeight: +style.getPropertyValue('--slidev-code-line-height').replace(/px/g, ''),
91
- lineDecorationsWidth: 0,
92
- lineNumbersMinChars: 0,
93
- scrollBeyondLastLine: false,
94
- scrollBeyondLastColumn: 0,
95
- automaticLayout: true,
96
- readOnly: props.readonly,
97
- theme: 'vitesse-dark',
98
- lineNumbers: props.lineNumbers as any,
99
- glyphMargin: false,
100
- scrollbar: {
101
- useShadows: false,
102
- vertical: 'hidden',
103
- horizontal: 'hidden',
104
- },
105
- overviewRulerLanes: 0,
106
- minimap: { enabled: false },
107
- enableSplitViewResizing: false,
108
- renderOverviewRuler: false,
109
- // renderSideBySide: false,
110
- ...editorOptions,
111
- })
112
- monacoEditor.setModel({
113
- original: model,
114
- modified: diffModel,
115
- })
116
- originalEditor = monacoEditor.getOriginalEditor()
117
- modifiedEditor = monacoEditor.getModifiedEditor()
118
-
119
- format = async () => {
120
- model.setValue((await formatCode(props.code, lang())).trim())
121
- diffModel.setValue((await formatCode(props.diff, lang())).trim())
122
- }
123
-
124
- // ctrl+s to format
125
- originalEditor.onKeyDown((e) => {
126
- if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
127
- e.preventDefault()
128
- format()
129
- }
130
- })
131
- modifiedEditor.onKeyDown((e) => {
132
- if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
133
- e.preventDefault()
134
- format()
135
- }
136
- })
137
-
138
- update = () => {
139
- monaco.editor.setTheme(props.dark
140
- ? (theme.dark || 'vitesse-dark')
141
- : (theme.light || 'vitesse-light'))
142
- styleObject.innerHTML = `:root { ${props.style} }`
143
-
144
- if (originalEditor.getValue().toString() !== props.code) {
145
- const selection = originalEditor.getSelection()
146
- originalEditor.setValue(props.code)
147
- if (selection)
148
- originalEditor.setSelection(selection)
149
- }
150
- originalEditor.updateOptions(props.editorOptions)
151
-
152
- if (modifiedEditor.getValue().toString() !== props.diff) {
153
- const selection = modifiedEditor.getSelection()
154
- modifiedEditor.setValue(props.diff)
155
- if (selection)
156
- modifiedEditor.setSelection(selection)
157
- }
158
- modifiedEditor.updateOptions(props.editorOptions)
159
- }
160
-
161
- diffModel.onDidChangeContent(() => {
162
- onCodeChange(diffModel.getValue().toString())
163
- })
164
-
165
- function onCodeChange(diff: string) {
166
- props.diff = diff
167
- post({ diff })
168
- }
169
- }
170
- else {
171
- // Normal editor
172
- originalEditor = monaco.editor.create(container, {
173
- model,
174
- tabSize: 2,
175
- insertSpaces: true,
176
- detectIndentation: false,
177
- folding: false,
178
- fontSize: +style.getPropertyValue('--slidev-code-font-size').replace(/px/g, ''),
179
- fontFamily: style.getPropertyValue('--slidev-code-font-family'),
180
- lineHeight: +style.getPropertyValue('--slidev-code-line-height').replace(/px/g, ''),
181
- lineDecorationsWidth: 0,
182
- lineNumbersMinChars: 0,
183
- scrollBeyondLastLine: false,
184
- scrollBeyondLastColumn: 0,
185
- automaticLayout: true,
186
- readOnly: props.readonly,
187
- theme: 'vitesse-dark',
188
- lineNumbers: props.lineNumbers as any,
189
- glyphMargin: false,
190
- scrollbar: {
191
- useShadows: false,
192
- vertical: 'hidden',
193
- horizontal: 'hidden',
194
- },
195
- overviewRulerLanes: 0,
196
- minimap: { enabled: false },
197
- ...editorOptions,
198
- })
199
-
200
- format = async () => {
201
- model.setValue((await formatCode(props.code, lang())).trim())
202
- }
203
-
204
- // ctrl+s to format
205
- originalEditor.onKeyDown((e) => {
206
- if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
207
- e.preventDefault()
208
- format()
209
- }
210
- })
211
-
212
- update = () => {
213
- monaco.editor.setTheme(props.dark
214
- ? (theme.dark || 'vitesse-dark')
215
- : (theme.light || 'vitesse-light'))
216
- styleObject.innerHTML = `:root { ${props.style} }`
217
-
218
- if (originalEditor.getValue().toString() !== props.code) {
219
- const selection = originalEditor.getSelection()
220
- originalEditor.setValue(props.code)
221
- if (selection)
222
- originalEditor.setSelection(selection)
223
- }
224
- originalEditor.updateOptions(props.editorOptions)
225
- }
226
- }
227
-
228
- originalEditor.onDidContentSizeChange(() => {
229
- post({ height: Math.max(originalEditor.getContentHeight(), modifiedEditor?.getContentHeight() ?? 0) })
230
- })
231
-
232
- model.onDidChangeContent(() => {
233
- onCodeChange(model.getValue().toString())
234
- })
235
-
236
- function onCodeChange(code: string) {
237
- props.code = code
238
- post({ code })
239
- }
240
-
241
- update()
242
-
243
- post({}, 'slidev-monaco-loaded')
244
- }
245
-
246
- window.addEventListener('message', (payload) => {
247
- if (payload.source === window)
248
- return
249
- if (payload.origin !== location.origin)
250
- return
251
- if (typeof payload.data !== 'string')
252
- return
253
- const { type, data, id } = JSON.parse(payload.data)
254
- if (type === 'slidev-monaco' && id === props.id) {
255
- Object.assign(props, data)
256
- update()
257
- }
258
- })
259
-
260
- start()