@slidev/client 0.48.0-beta.14 → 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.
- package/builtin/Arrow.vue +2 -4
- package/builtin/Mermaid.vue +2 -2
- package/builtin/Monaco.vue +109 -93
- package/builtin/ShikiMagicMove.vue +6 -4
- package/internals/NoteDisplay.vue +31 -24
- package/internals/{NoteEditor.vue → NoteEditable.vue} +27 -21
- package/logic/nav.ts +3 -6
- package/modules/mermaid.ts +6 -7
- package/package.json +8 -5
- package/pages/overview.vue +12 -2
- package/pages/presenter.vue +2 -2
- package/setup/monaco.ts +103 -44
- package/setup/root.ts +2 -2
- package/shim.d.ts +1 -1
- package/styles/monaco.css +27 -0
- package/iframes/monaco/index.css +0 -28
- package/iframes/monaco/index.html +0 -7
- package/iframes/monaco/index.ts +0 -260
package/builtin/Arrow.vue
CHANGED
|
@@ -9,7 +9,7 @@ Simple Arrow
|
|
|
9
9
|
-->
|
|
10
10
|
|
|
11
11
|
<script setup lang="ts">
|
|
12
|
-
import {
|
|
12
|
+
import { makeId } from '../logic/utils'
|
|
13
13
|
|
|
14
14
|
defineProps<{
|
|
15
15
|
x1: number | string
|
|
@@ -20,9 +20,7 @@ defineProps<{
|
|
|
20
20
|
color?: string
|
|
21
21
|
}>()
|
|
22
22
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const id = nanoid()
|
|
23
|
+
const id = makeId()
|
|
26
24
|
</script>
|
|
27
25
|
|
|
28
26
|
<template>
|
package/builtin/Mermaid.vue
CHANGED
|
@@ -19,7 +19,7 @@ import ShadowRoot from '../internals/ShadowRoot.vue'
|
|
|
19
19
|
import { isDark } from '../logic/dark'
|
|
20
20
|
|
|
21
21
|
const props = defineProps<{
|
|
22
|
-
|
|
22
|
+
codeLz: string
|
|
23
23
|
scale?: number
|
|
24
24
|
theme?: string
|
|
25
25
|
}>()
|
|
@@ -37,7 +37,7 @@ watchEffect(async (onCleanup) => {
|
|
|
37
37
|
error.value = null
|
|
38
38
|
try {
|
|
39
39
|
const svg = await renderMermaid(
|
|
40
|
-
props.
|
|
40
|
+
props.codeLz || '',
|
|
41
41
|
{
|
|
42
42
|
theme: props.theme || (isDark.value ? 'dark' : undefined),
|
|
43
43
|
...vm!.attrs,
|
package/builtin/Monaco.vue
CHANGED
|
@@ -12,122 +12,138 @@ Learn more: https://sli.dev/guide/syntax.html#monaco-editor
|
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
15
|
+
import * as monaco from 'monaco-editor'
|
|
16
|
+
import { computed, nextTick, onMounted, ref } from 'vue'
|
|
17
|
+
import { debounce } from '@antfu/utils'
|
|
18
|
+
import { decompressFromBase64 } from 'lz-string'
|
|
19
|
+
import setup from '../setup/monaco'
|
|
20
|
+
import { makeId } from '../logic/utils'
|
|
21
21
|
|
|
22
22
|
const props = withDefaults(defineProps<{
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
codeLz: string
|
|
24
|
+
diffLz?: string
|
|
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: '
|
|
36
|
+
height: 'initial',
|
|
37
|
+
ata: true,
|
|
36
38
|
})
|
|
37
39
|
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const diff = ref(props.diff ? decode(props.diff).trimEnd() : 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
|
|
51
|
-
'
|
|
52
|
-
'
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
<
|
|
146
|
+
<div ref="outer" class="slidev-monaco-container" :style="{ height }">
|
|
147
|
+
<div ref="container" class="absolute inset-0.5" />
|
|
148
|
+
</div>
|
|
133
149
|
</template>
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
import { ShikiMagicMovePrecompiled } from 'shiki-magic-move/vue'
|
|
3
3
|
import type { KeyedTokensInfo } from 'shiki-magic-move/types'
|
|
4
4
|
import { onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
5
|
+
import { decompressFromBase64 } from 'lz-string'
|
|
5
6
|
import { useSlideContext } from '../context'
|
|
6
7
|
import { makeId } from '../logic/utils'
|
|
7
8
|
|
|
8
9
|
import 'shiki-magic-move/style.css'
|
|
9
10
|
|
|
10
11
|
const props = defineProps<{
|
|
11
|
-
|
|
12
|
+
stepsLz: string
|
|
12
13
|
at?: string | number
|
|
13
14
|
}>()
|
|
14
15
|
|
|
16
|
+
const steps = JSON.parse(decompressFromBase64(props.stepsLz)) as KeyedTokensInfo[]
|
|
15
17
|
const { $clicksContext: clicks, $scale: scale } = useSlideContext()
|
|
16
18
|
const id = makeId()
|
|
17
19
|
const index = ref(0)
|
|
@@ -24,14 +26,14 @@ onMounted(() => {
|
|
|
24
26
|
if (!clicks || clicks.disabled)
|
|
25
27
|
return
|
|
26
28
|
|
|
27
|
-
const { start, end, delta } = clicks.resolve(props.at || '+1',
|
|
29
|
+
const { start, end, delta } = clicks.resolve(props.at || '+1', steps.length - 1)
|
|
28
30
|
clicks.register(id, { max: end, delta })
|
|
29
31
|
|
|
30
32
|
watchEffect(() => {
|
|
31
33
|
if (clicks.disabled)
|
|
32
|
-
index.value =
|
|
34
|
+
index.value = steps.length - 1
|
|
33
35
|
else
|
|
34
|
-
index.value = Math.min(Math.max(0, clicks.current - start + 1),
|
|
36
|
+
index.value = Math.min(Math.max(0, clicks.current - start + 1), steps.length - 1)
|
|
35
37
|
})
|
|
36
38
|
})
|
|
37
39
|
</script>
|
|
@@ -12,7 +12,10 @@ const props = defineProps<{
|
|
|
12
12
|
autoScroll?: boolean
|
|
13
13
|
}>()
|
|
14
14
|
|
|
15
|
-
defineEmits
|
|
15
|
+
const emit = defineEmits<{
|
|
16
|
+
(type: 'markerDblclick', e: MouseEvent, clicks: number): void
|
|
17
|
+
(type: 'markerClick', e: MouseEvent, clicks: number): void
|
|
18
|
+
}>()
|
|
16
19
|
|
|
17
20
|
const withClicks = computed(() => props.clicksContext?.current != null && props.noteHtml?.includes('slidev-note-click-mark'))
|
|
18
21
|
const noteDisplay = ref<HTMLElement | null>(null)
|
|
@@ -21,16 +24,13 @@ const CLASS_FADE = 'slidev-note-fade'
|
|
|
21
24
|
const CLASS_MARKER = 'slidev-note-click-mark'
|
|
22
25
|
|
|
23
26
|
function highlightNote() {
|
|
24
|
-
if (!noteDisplay.value || !withClicks.value
|
|
27
|
+
if (!noteDisplay.value || !withClicks.value)
|
|
25
28
|
return
|
|
26
29
|
|
|
27
|
-
const
|
|
30
|
+
const markers = Array.from(noteDisplay.value.querySelectorAll(`.${CLASS_MARKER}`)) as HTMLElement[]
|
|
31
|
+
|
|
32
|
+
const current = +(props.clicksContext?.current ?? CLICKS_MAX)
|
|
28
33
|
const disabled = current < 0 || current >= CLICKS_MAX
|
|
29
|
-
if (disabled) {
|
|
30
|
-
Array.from(noteDisplay.value.querySelectorAll('*'))
|
|
31
|
-
.forEach(el => el.classList.remove(CLASS_FADE))
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
34
|
|
|
35
35
|
const nodeToIgnores = new Set<Element>()
|
|
36
36
|
function ignoreParent(node: Element) {
|
|
@@ -41,7 +41,6 @@ function highlightNote() {
|
|
|
41
41
|
ignoreParent(node.parentElement)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const markers = Array.from(noteDisplay.value.querySelectorAll(`.${CLASS_MARKER}`)) as HTMLElement[]
|
|
45
44
|
const markersMap = new Map<number, HTMLElement>()
|
|
46
45
|
|
|
47
46
|
// Convert all sibling text nodes to spans, so we attach classes to them
|
|
@@ -78,25 +77,36 @@ function highlightNote() {
|
|
|
78
77
|
|
|
79
78
|
// Apply
|
|
80
79
|
for (const [count, els] of segments) {
|
|
81
|
-
|
|
82
|
-
CLASS_FADE
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
if (disabled) {
|
|
81
|
+
els.forEach(el => el.classList.remove(CLASS_FADE))
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
els.forEach(el => el.classList.toggle(
|
|
85
|
+
CLASS_FADE,
|
|
86
|
+
nodeToIgnores.has(el)
|
|
87
|
+
? false
|
|
88
|
+
: count !== current,
|
|
89
|
+
))
|
|
90
|
+
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
for (const [clicks, marker] of markersMap) {
|
|
90
94
|
marker.classList.remove(CLASS_FADE)
|
|
91
|
-
marker.classList.toggle(`${CLASS_MARKER}-past`, clicks < current)
|
|
92
|
-
marker.classList.toggle(`${CLASS_MARKER}-active`, clicks === current)
|
|
93
|
-
marker.classList.toggle(`${CLASS_MARKER}-next`, clicks === current + 1)
|
|
94
|
-
marker.classList.toggle(`${CLASS_MARKER}-future`, clicks > current + 1)
|
|
95
|
-
marker.
|
|
95
|
+
marker.classList.toggle(`${CLASS_MARKER}-past`, disabled ? false : clicks < current)
|
|
96
|
+
marker.classList.toggle(`${CLASS_MARKER}-active`, disabled ? false : clicks === current)
|
|
97
|
+
marker.classList.toggle(`${CLASS_MARKER}-next`, disabled ? false : clicks === current + 1)
|
|
98
|
+
marker.classList.toggle(`${CLASS_MARKER}-future`, disabled ? false : clicks > current + 1)
|
|
99
|
+
marker.ondblclick = (e) => {
|
|
100
|
+
emit('markerDblclick', e, clicks)
|
|
101
|
+
if (e.defaultPrevented)
|
|
102
|
+
return
|
|
96
103
|
props.clicksContext!.current = clicks
|
|
97
104
|
e.stopPropagation()
|
|
98
105
|
e.stopImmediatePropagation()
|
|
99
|
-
}
|
|
106
|
+
}
|
|
107
|
+
marker.onclick = (e) => {
|
|
108
|
+
emit('markerClick', e, clicks)
|
|
109
|
+
}
|
|
100
110
|
|
|
101
111
|
if (props.autoScroll && clicks === current)
|
|
102
112
|
marker.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
|
@@ -124,14 +134,12 @@ onMounted(() => {
|
|
|
124
134
|
ref="noteDisplay"
|
|
125
135
|
class="prose overflow-auto outline-none slidev-note"
|
|
126
136
|
:class="[props.class, withClicks ? 'slidev-note-with-clicks' : '']"
|
|
127
|
-
@click="$emit('click')"
|
|
128
137
|
v-html="noteHtml"
|
|
129
138
|
/>
|
|
130
139
|
<div
|
|
131
140
|
v-else-if="note"
|
|
132
141
|
class="prose overflow-auto outline-none slidev-note"
|
|
133
142
|
:class="props.class"
|
|
134
|
-
@click="$emit('click')"
|
|
135
143
|
>
|
|
136
144
|
<p v-text="note" />
|
|
137
145
|
</div>
|
|
@@ -139,7 +147,6 @@ onMounted(() => {
|
|
|
139
147
|
v-else
|
|
140
148
|
class="prose overflow-auto outline-none opacity-50 italic select-none slidev-note"
|
|
141
149
|
:class="props.class"
|
|
142
|
-
@click="$emit('click')"
|
|
143
150
|
>
|
|
144
151
|
<p v-text="props.placeholder || 'No notes.'" />
|
|
145
152
|
</div>
|
|
@@ -30,9 +30,12 @@ const props = defineProps({
|
|
|
30
30
|
},
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
const emit = defineEmits
|
|
34
|
-
'update:editing',
|
|
35
|
-
|
|
33
|
+
const emit = defineEmits<{
|
|
34
|
+
(type: 'update:editing', value: boolean): void
|
|
35
|
+
(type: 'markerDblclick', e: MouseEvent, clicks: number): void
|
|
36
|
+
(type: 'markerClick', e: MouseEvent, clicks: number): void
|
|
37
|
+
}>()
|
|
38
|
+
|
|
36
39
|
const editing = useVModel(props, 'editing', emit, { passive: true })
|
|
37
40
|
|
|
38
41
|
const { info, update } = useDynamicSlideInfo(props.no)
|
|
@@ -40,9 +43,12 @@ const { info, update } = useDynamicSlideInfo(props.no)
|
|
|
40
43
|
const note = ref('')
|
|
41
44
|
let timer: any
|
|
42
45
|
|
|
46
|
+
// Send back the note on changes
|
|
43
47
|
const { ignoreUpdates } = ignorableWatch(
|
|
44
48
|
note,
|
|
45
49
|
(v) => {
|
|
50
|
+
if (!editing.value)
|
|
51
|
+
return
|
|
46
52
|
const id = props.no
|
|
47
53
|
clearTimeout(timer)
|
|
48
54
|
timer = setTimeout(() => {
|
|
@@ -51,46 +57,44 @@ const { ignoreUpdates } = ignorableWatch(
|
|
|
51
57
|
},
|
|
52
58
|
)
|
|
53
59
|
|
|
60
|
+
// Update note value when info changes
|
|
54
61
|
watch(
|
|
55
|
-
info,
|
|
56
|
-
(
|
|
62
|
+
() => info.value?.note,
|
|
63
|
+
(value = '') => {
|
|
57
64
|
if (editing.value)
|
|
58
65
|
return
|
|
59
66
|
clearTimeout(timer)
|
|
60
67
|
ignoreUpdates(() => {
|
|
61
|
-
note.value =
|
|
68
|
+
note.value = value
|
|
62
69
|
})
|
|
63
70
|
},
|
|
64
71
|
{ immediate: true, flush: 'sync' },
|
|
65
72
|
)
|
|
66
73
|
|
|
67
|
-
const
|
|
74
|
+
const inputEl = ref<HTMLTextAreaElement>()
|
|
75
|
+
const inputHeight = ref<number | null>()
|
|
68
76
|
|
|
69
77
|
watchEffect(() => {
|
|
70
78
|
if (editing.value)
|
|
71
|
-
|
|
79
|
+
inputEl.value?.focus()
|
|
72
80
|
})
|
|
73
81
|
|
|
74
|
-
onClickOutside(
|
|
82
|
+
onClickOutside(inputEl, () => {
|
|
75
83
|
editing.value = false
|
|
76
84
|
})
|
|
77
85
|
|
|
78
|
-
function
|
|
79
|
-
if (!props.autoHeight || !
|
|
86
|
+
function calculateEditorHeight() {
|
|
87
|
+
if (!props.autoHeight || !inputEl.value || !editing.value)
|
|
80
88
|
return
|
|
81
|
-
if (
|
|
82
|
-
|
|
89
|
+
if (inputEl.value.scrollHeight > inputEl.value.clientHeight)
|
|
90
|
+
inputEl.value.style.height = `${inputEl.value.scrollHeight}px`
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
const inputHeight = ref<number | null>()
|
|
86
|
-
|
|
87
93
|
watch(
|
|
88
94
|
note,
|
|
89
|
-
() => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
})
|
|
93
|
-
},
|
|
95
|
+
() => nextTick(() => {
|
|
96
|
+
calculateEditorHeight()
|
|
97
|
+
}),
|
|
94
98
|
{ flush: 'post', immediate: true },
|
|
95
99
|
)
|
|
96
100
|
</script>
|
|
@@ -105,10 +109,12 @@ watch(
|
|
|
105
109
|
:note-html="info?.noteHTML"
|
|
106
110
|
:clicks-context="clicksContext"
|
|
107
111
|
:auto-scroll="!autoHeight"
|
|
112
|
+
@marker-click="(e, clicks) => emit('markerClick', e, clicks)"
|
|
113
|
+
@marker-dblclick="(e, clicks) => emit('markerDblclick', e, clicks)"
|
|
108
114
|
/>
|
|
109
115
|
<textarea
|
|
110
116
|
v-else
|
|
111
|
-
ref="
|
|
117
|
+
ref="inputEl"
|
|
112
118
|
v-model="note"
|
|
113
119
|
class="prose resize-none overflow-auto outline-none bg-transparent block border-primary border-2"
|
|
114
120
|
style="line-height: 1.75;"
|
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
|
-
|
|
139
|
-
|
|
140
|
-
return
|
|
138
|
+
pointerTypes: ['touch'],
|
|
139
|
+
onSwipeStart() {
|
|
141
140
|
if (isDrawing.value)
|
|
142
141
|
return
|
|
143
142
|
swipeBegin.value = timestamp()
|
|
144
143
|
},
|
|
145
|
-
onSwipeEnd(
|
|
146
|
-
if (e.pointerType !== 'touch')
|
|
147
|
-
return
|
|
144
|
+
onSwipeEnd() {
|
|
148
145
|
if (!swipeBegin.value)
|
|
149
146
|
return
|
|
150
147
|
if (isDrawing.value)
|
package/modules/mermaid.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import mermaid from 'mermaid/dist/mermaid.esm.mjs'
|
|
2
|
-
import {
|
|
3
|
-
import { decode } from 'js-base64'
|
|
2
|
+
import { decompressFromBase64 } from 'lz-string'
|
|
4
3
|
import { clearUndefined } from '@antfu/utils'
|
|
5
4
|
import setupMermaid from '../setup/mermaid'
|
|
5
|
+
import { makeId } from '../logic/utils'
|
|
6
6
|
|
|
7
7
|
mermaid.startOnLoad = false
|
|
8
8
|
mermaid.initialize({ startOnLoad: false })
|
|
9
9
|
|
|
10
|
-
const nanoid = customAlphabet('abcedfghicklmn', 10)
|
|
11
10
|
const cache = new Map<string, string>()
|
|
12
11
|
|
|
13
|
-
export async function renderMermaid(
|
|
14
|
-
const key =
|
|
12
|
+
export async function renderMermaid(lzEncoded: string, options: any) {
|
|
13
|
+
const key = lzEncoded + JSON.stringify(options)
|
|
15
14
|
const _cache = cache.get(key)
|
|
16
15
|
if (_cache)
|
|
17
16
|
return _cache
|
|
@@ -21,8 +20,8 @@ export async function renderMermaid(encoded: string, options: any) {
|
|
|
21
20
|
...clearUndefined(setupMermaid() || {}),
|
|
22
21
|
...clearUndefined(options),
|
|
23
22
|
})
|
|
24
|
-
const code =
|
|
25
|
-
const id =
|
|
23
|
+
const code = decompressFromBase64(lzEncoded)
|
|
24
|
+
const id = makeId()
|
|
26
25
|
const { svg } = await mermaid.render(id, code)
|
|
27
26
|
cache.set(key, svg)
|
|
28
27
|
return svg
|
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.
|
|
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",
|
|
@@ -42,21 +44,22 @@
|
|
|
42
44
|
"file-saver": "^2.0.5",
|
|
43
45
|
"floating-vue": "^5.2.2",
|
|
44
46
|
"fuse.js": "^7.0.0",
|
|
45
|
-
"js-base64": "^3.7.7",
|
|
46
47
|
"js-yaml": "^4.1.0",
|
|
47
48
|
"katex": "^0.16.9",
|
|
49
|
+
"lz-string": "^1.5.0",
|
|
48
50
|
"mermaid": "^10.8.0",
|
|
49
|
-
"monaco-editor": "^0.
|
|
51
|
+
"monaco-editor": "^0.46.0",
|
|
50
52
|
"nanoid": "^5.0.6",
|
|
51
53
|
"prettier": "^3.2.5",
|
|
52
54
|
"recordrtc": "^5.6.2",
|
|
53
55
|
"resolve": "^1.22.8",
|
|
56
|
+
"shiki": "^1.1.7",
|
|
54
57
|
"shiki-magic-move": "^0.1.0",
|
|
55
58
|
"unocss": "^0.58.5",
|
|
56
59
|
"vue": "^3.4.20",
|
|
57
60
|
"vue-router": "^4.3.0",
|
|
58
|
-
"@slidev/
|
|
59
|
-
"@slidev/
|
|
61
|
+
"@slidev/parser": "0.48.0-beta.16",
|
|
62
|
+
"@slidev/types": "0.48.0-beta.16"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
65
|
"vite": "^5.1.4"
|
package/pages/overview.vue
CHANGED
|
@@ -12,7 +12,7 @@ import SlideContainer from '../internals/SlideContainer.vue'
|
|
|
12
12
|
import SlideWrapper from '../internals/SlideWrapper'
|
|
13
13
|
import DrawingPreview from '../internals/DrawingPreview.vue'
|
|
14
14
|
import IconButton from '../internals/IconButton.vue'
|
|
15
|
-
import
|
|
15
|
+
import NoteEditable from '../internals/NoteEditable.vue'
|
|
16
16
|
import OverviewClicksSlider from '../internals/OverviewClicksSlider.vue'
|
|
17
17
|
import { CLICKS_MAX } from '../constants'
|
|
18
18
|
|
|
@@ -80,6 +80,15 @@ function scrollToSlide(idx: number) {
|
|
|
80
80
|
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function onMarkerClick(e: MouseEvent, clicks: number, route: RouteRecordRaw) {
|
|
84
|
+
const ctx = getClicksContext(route)
|
|
85
|
+
if (ctx.current === clicks)
|
|
86
|
+
ctx.current = CLICKS_MAX
|
|
87
|
+
else
|
|
88
|
+
ctx.current = clicks
|
|
89
|
+
e.preventDefault()
|
|
90
|
+
}
|
|
91
|
+
|
|
83
92
|
onMounted(() => {
|
|
84
93
|
nextTick(() => {
|
|
85
94
|
checkActiveBlocks()
|
|
@@ -192,7 +201,7 @@ onMounted(() => {
|
|
|
192
201
|
<carbon:pen />
|
|
193
202
|
</IconButton>
|
|
194
203
|
</div>
|
|
195
|
-
<
|
|
204
|
+
<NoteEditable
|
|
196
205
|
:no="idx"
|
|
197
206
|
class="max-w-250 w-250 text-lg rounded p3"
|
|
198
207
|
:auto-height="true"
|
|
@@ -200,6 +209,7 @@ onMounted(() => {
|
|
|
200
209
|
:clicks-context="getClicksContext(route)"
|
|
201
210
|
@dblclick="edittingNote !== idx ? edittingNote = idx : null"
|
|
202
211
|
@update:editing="edittingNote = null"
|
|
212
|
+
@marker-click="(e, clicks) => onMarkerClick(e, clicks, route)"
|
|
203
213
|
/>
|
|
204
214
|
<div
|
|
205
215
|
v-if="wordCounts[idx] > 0"
|
package/pages/presenter.vue
CHANGED
|
@@ -15,7 +15,7 @@ import SlideWrapper from '../internals/SlideWrapper'
|
|
|
15
15
|
import SlideContainer from '../internals/SlideContainer.vue'
|
|
16
16
|
import NavControls from '../internals/NavControls.vue'
|
|
17
17
|
import QuickOverview from '../internals/QuickOverview.vue'
|
|
18
|
-
import
|
|
18
|
+
import NoteEditable from '../internals/NoteEditable.vue'
|
|
19
19
|
import NoteStatic from '../internals/NoteStatic.vue'
|
|
20
20
|
import Goto from '../internals/Goto.vue'
|
|
21
21
|
import SlidesShow from '../internals/SlidesShow.vue'
|
|
@@ -130,7 +130,7 @@ onMounted(() => {
|
|
|
130
130
|
<SideEditor />
|
|
131
131
|
</div>
|
|
132
132
|
<div v-else class="grid-section note grid grid-rows-[1fr_min-content] overflow-hidden">
|
|
133
|
-
<
|
|
133
|
+
<NoteEditable
|
|
134
134
|
v-if="__DEV__"
|
|
135
135
|
:key="`edit-${currentSlideId}`"
|
|
136
136
|
v-model:editing="notesEditing"
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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/setup/root.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* __imports__ */
|
|
2
2
|
import { watch } from 'vue'
|
|
3
3
|
import { useHead } from '@unhead/vue'
|
|
4
|
-
import { nanoid } from 'nanoid'
|
|
5
4
|
import { configs } from '../env'
|
|
6
5
|
import { initSharedState, onPatch, patch } from '../state/shared'
|
|
7
6
|
import { initDrawingState } from '../state/drawings'
|
|
@@ -9,6 +8,7 @@ import { clicksContext, currentPage, getPath, isNotesViewer, isPresenter } from
|
|
|
9
8
|
import { router } from '../routes'
|
|
10
9
|
import { TRUST_ORIGINS } from '../constants'
|
|
11
10
|
import { skipTransition } from '../composables/hmr'
|
|
11
|
+
import { makeId } from '../logic/utils'
|
|
12
12
|
|
|
13
13
|
export default function setupRoot() {
|
|
14
14
|
// @ts-expect-error injected in runtime
|
|
@@ -25,7 +25,7 @@ export default function setupRoot() {
|
|
|
25
25
|
initSharedState(`${title} - shared`)
|
|
26
26
|
initDrawingState(`${title} - drawings`)
|
|
27
27
|
|
|
28
|
-
const id = `${location.origin}_${
|
|
28
|
+
const id = `${location.origin}_${makeId()}`
|
|
29
29
|
|
|
30
30
|
// update shared state
|
|
31
31
|
function updateSharedState() {
|
package/shim.d.ts
CHANGED
|
@@ -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
|
+
}
|
package/iframes/monaco/index.css
DELETED
|
@@ -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
|
-
}
|
package/iframes/monaco/index.ts
DELETED
|
@@ -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()
|