@slidev/client 0.50.0-beta.6 → 0.50.0-beta.8

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.
@@ -197,6 +197,7 @@ onMounted(async () => {
197
197
  })
198
198
 
199
199
  nextTick(() => monaco.editor.remeasureFonts())
200
+ setTimeout(() => monaco.editor.remeasureFonts(), 1000)
200
201
  })
201
202
  </script>
202
203
 
package/builtin/Tweet.vue CHANGED
@@ -55,7 +55,7 @@ onMounted(() => {
55
55
  <div ref="tweet" class="tweet slidev-tweet">
56
56
  <div v-if="!loaded || tweetNotFound" class="w-30 h-30 my-10px bg-gray-400 bg-opacity-10 rounded-lg flex opacity-50">
57
57
  <div class="m-auto animate-pulse text-4xl">
58
- <carbon:logo-twitter />
58
+ <div class="i-carbon:logo-twitter" />
59
59
  <span v-if="tweetNotFound">Could not load tweet with id="{{ props.id }}"</span>
60
60
  </div>
61
61
  </div>
@@ -69,7 +69,7 @@ export function useDragElementsUpdater(no: number) {
69
69
 
70
70
  section = type === 'prop'
71
71
  // eslint-disable-next-line regexp/no-super-linear-backtracking
72
- ? section.replace(/<(v-?drag-?\w*)(.*?)(\/)?>/gi, (full, tag, attrs, selfClose, index) => {
72
+ ? section.replace(/<(v-?drag-?\w*)(.*?)(\/)?>/gi, (full, tag, attrs, selfClose = '', index) => {
73
73
  if (index === idx) {
74
74
  replaced = true
75
75
  const posMatch = attrs.match(/pos=".*?"/)
@@ -0,0 +1,20 @@
1
+ import { useInterval } from '@vueuse/core'
2
+ import { computed } from 'vue'
3
+
4
+ export function useTimer() {
5
+ const { counter, isActive, reset, pause, resume } = useInterval(1000, { controls: true })
6
+
7
+ const timer = computed(() => {
8
+ const passed = counter.value
9
+ const sec = Math.floor(passed % 60).toString().padStart(2, '0')
10
+ const min = Math.floor(passed / 60).toString().padStart(2, '0')
11
+ return `${min}:${sec}`
12
+ })
13
+
14
+ return {
15
+ timer,
16
+ isTimerAvctive: isActive,
17
+ resetTimer: reset,
18
+ toggleTimer: () => (isActive.value ? pause() : resume()),
19
+ }
20
+ }
package/constants.ts CHANGED
@@ -45,6 +45,7 @@ export const FRONTMATTER_FIELDS = [
45
45
  'transition',
46
46
  'zoom',
47
47
  'dragPos',
48
+ 'lang',
48
49
  ]
49
50
 
50
51
  export const HEADMATTER_FIELDS = [
@@ -42,7 +42,7 @@ function onMousedown() {
42
42
  :class="length && props.clicksContext.isMounted ? '' : 'op50'"
43
43
  >
44
44
  <div class="flex gap-0.2 items-center min-w-16 font-mono mr1">
45
- <carbon:cursor-1 text-sm op50 />
45
+ <div class="i-carbon:cursor-1 text-sm op50" />
46
46
  <template v-if="current >= 0 && current !== CLICKS_MAX && active">
47
47
  <div flex-auto />
48
48
  <span text-primary>{{ current }}</span>
@@ -119,7 +119,7 @@ else if (autorun)
119
119
  </div>
120
120
  <div v-if="code.trim()" class="absolute right-1 top-1 max-h-full flex gap-1">
121
121
  <IconButton class="w-8 h-8 max-h-full flex justify-center items-center" title="Run code" @click="triggerRun">
122
- <carbon:play />
122
+ <div class="i-carbon:play" />
123
123
  </IconButton>
124
124
  </div>
125
125
  </template>
@@ -69,12 +69,13 @@ const top = computed(() => {
69
69
  <div v-if="item === 'separator'" :key="index" class="w-full my1 border-t border-main" />
70
70
  <div
71
71
  v-else-if="item.small"
72
- class="p-2 w-[40px] h-[40px] inline-block text-center cursor-pointer rounded"
72
+ class="p-2 w-[40px] h-[40px] inline-block text-center cursor-pointer rounded flex"
73
73
  :class="item.disabled ? `op40` : `hover:bg-active`"
74
74
  :title="(item.label as string)"
75
75
  @click="item.action"
76
76
  >
77
- <component :is="item.icon" />
77
+ <div v-if="typeof item.icon === 'string'" :class="item.icon" class="text-1.2em ma" />
78
+ <component :is="item.icon" v-else />
78
79
  </div>
79
80
  <div
80
81
  v-else
@@ -82,8 +83,9 @@ const top = computed(() => {
82
83
  :class="item.disabled ? `op40` : `hover:bg-active`"
83
84
  @click="item.action"
84
85
  >
85
- <div class="mx-auto">
86
- <component :is="item.icon" />
86
+ <div class="mx-auto flex">
87
+ <div v-if="typeof item.icon === 'string'" :class="item.icon" class="text-1.2em ma" />
88
+ <component :is="item.icon" v-else />
87
89
  </div>
88
90
  <div v-if="typeof item.label === 'string'">
89
91
  {{ item.label }}
@@ -49,7 +49,7 @@ function setBrushColor(color: typeof brush.color) {
49
49
  :initial-y="10"
50
50
  >
51
51
  <IconButton title="Draw with stylus" :class="{ shallow: drawingMode !== 'stylus' }" @click="setDrawingMode('stylus')">
52
- <carbon:pen />
52
+ <div class="i-carbon:pen" />
53
53
  </IconButton>
54
54
  <IconButton title="Draw a line" :class="{ shallow: drawingMode !== 'line' }" @click="setDrawingMode('line')">
55
55
  <svg width="1em" height="1em" class="-mt-0.5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
@@ -57,16 +57,16 @@ function setBrushColor(color: typeof brush.color) {
57
57
  </svg>
58
58
  </IconButton>
59
59
  <IconButton title="Draw an arrow" :class="{ shallow: drawingMode !== 'arrow' }" @click="setDrawingMode('arrow')">
60
- <carbon:arrow-up-right />
60
+ <div class="i-carbon:arrow-up-right" />
61
61
  </IconButton>
62
62
  <IconButton title="Draw an ellipse" :class="{ shallow: drawingMode !== 'ellipse' }" @click="setDrawingMode('ellipse')">
63
- <carbon:radio-button />
63
+ <div class="i-carbon:radio-button" />
64
64
  </IconButton>
65
65
  <IconButton title="Draw a rectangle" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
66
- <carbon:checkbox />
66
+ <div class="i-carbon:checkbox" />
67
67
  </IconButton>
68
68
  <IconButton title="Erase" :class="{ shallow: drawingMode !== 'eraseLine' }" @click="setDrawingMode('eraseLine')">
69
- <carbon:erase />
69
+ <div class="i-carbon:erase" />
70
70
  </IconButton>
71
71
 
72
72
  <VerticalDivider />
@@ -107,19 +107,19 @@ function setBrushColor(color: typeof brush.color) {
107
107
  <VerticalDivider />
108
108
 
109
109
  <IconButton title="Undo" :class="{ disabled: !canUndo }" @click="undo()">
110
- <carbon:undo />
110
+ <div class="i-carbon:undo" />
111
111
  </IconButton>
112
112
  <IconButton title="Redo" :class="{ disabled: !canRedo }" @click="redo()">
113
- <carbon:redo />
113
+ <div class="i-carbon:redo" />
114
114
  </IconButton>
115
115
  <IconButton title="Delete" :class="{ disabled: !canClear }" @click="clear()">
116
- <carbon:trash-can />
116
+ <div class="i-carbon:trash-can" />
117
117
  </IconButton>
118
118
 
119
119
  <VerticalDivider />
120
120
  <IconButton :title="drawingPinned ? 'Unpin drawing' : 'Pin drawing'" :class="{ shallow: !drawingPinned }" @click="drawingPinned = !drawingPinned">
121
- <carbon:pin-filled v-show="drawingPinned" class="transform -rotate-45" />
122
- <carbon:pin v-show="!drawingPinned" />
121
+ <div v-show="drawingPinned" class="i-carbon:pin-filled transform -rotate-45" />
122
+ <div v-show="!drawingPinned" class="i-carbon:pin" />
123
123
  </IconButton>
124
124
  <IconButton
125
125
  v-if="drawingEnabled"
@@ -127,8 +127,8 @@ function setBrushColor(color: typeof brush.color) {
127
127
  :class="{ shallow: !drawingEnabled }"
128
128
  @click="drawingEnabled = !drawingEnabled"
129
129
  >
130
- <carbon:error v-show="drawingPinned" />
131
- <carbon:close-outline v-show="!drawingPinned" />
130
+ <div v-show="drawingPinned" class="i-carbon:error" />
131
+ <div v-show="!drawingPinned" class="i-carbon:close-outline" />
132
132
  </IconButton>
133
133
  </Draggable>
134
134
  </template>
@@ -63,17 +63,17 @@ if (__SLIDEV_FEATURE_RECORD__)
63
63
  @mouseleave="onMouseLeave"
64
64
  >
65
65
  <IconButton v-if="!isEmbedded" :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
66
- <carbon:minimize v-if="isFullscreen" />
67
- <carbon:maximize v-else />
66
+ <div v-if="isFullscreen" class="i-carbon:minimize" />
67
+ <div v-else class="i-carbon:maximize" />
68
68
  </IconButton>
69
69
  <IconButton :class="{ disabled: !hasPrev }" title="Go to previous slide" @click="prev">
70
- <carbon:arrow-left />
70
+ <div class="i-carbon:arrow-left" />
71
71
  </IconButton>
72
72
  <IconButton :class="{ disabled: !hasNext }" title="Go to next slide" @click="next">
73
- <carbon:arrow-right />
73
+ <div class="i-carbon:arrow-right" />
74
74
  </IconButton>
75
75
  <IconButton v-if="!isEmbedded" title="Show slide overview" @click="toggleOverview()">
76
- <carbon:apps />
76
+ <div class="i-carbon:apps" />
77
77
  </IconButton>
78
78
  <IconButton
79
79
  v-if="!isColorSchemaConfigured"
@@ -104,7 +104,7 @@ if (__SLIDEV_FEATURE_RECORD__)
104
104
 
105
105
  <template v-if="__SLIDEV_FEATURE_DRAWINGS__ && (!configs.drawings.presenterOnly || isPresenter) && !isEmbedded">
106
106
  <IconButton class="relative" :title="drawingEnabled ? 'Hide drawing toolbar' : 'Show drawing toolbar'" @click="drawingEnabled = !drawingEnabled">
107
- <carbon:pen />
107
+ <div class="i-carbon:pen" />
108
108
  <div
109
109
  v-if="drawingEnabled"
110
110
  class="absolute left-1 right-1 bottom-0 h-0.7 rounded-full"
@@ -116,10 +116,10 @@ if (__SLIDEV_FEATURE_RECORD__)
116
116
 
117
117
  <template v-if="!isEmbedded">
118
118
  <IconButton v-if="isPresenter" title="Play Mode" @click="exitPresenter">
119
- <carbon:presentation-file />
119
+ <div class="i-carbon:presentation-file" />
120
120
  </IconButton>
121
121
  <IconButton v-if="__SLIDEV_FEATURE_PRESENTER__ && isPresenterAvailable" title="Presenter Mode" @click="enterPresenter">
122
- <carbon:user-speaker />
122
+ <div class="i-carbon:user-speaker" />
123
123
  </IconButton>
124
124
 
125
125
  <IconButton
@@ -128,17 +128,17 @@ if (__SLIDEV_FEATURE_RECORD__)
128
128
  class="lt-md:hidden"
129
129
  @click="showEditor = !showEditor"
130
130
  >
131
- <carbon:text-annotation-toggle />
131
+ <div class="i-carbon:text-annotation-toggle" />
132
132
  </IconButton>
133
133
 
134
134
  <IconButton v-if="isPresenter" title="Toggle Presenter Layout" class="aspect-ratio-initial" @click="togglePresenterLayout">
135
- <carbon:template />
135
+ <div class="i-carbon:template" />
136
136
  {{ presenterLayout }}
137
137
  </IconButton>
138
138
  </template>
139
139
  <template v-if="!__DEV__">
140
140
  <IconButton v-if="configs.download" title="Download as PDF" @click="downloadPDF">
141
- <carbon:download />
141
+ <div class="i-carbon:download" />
142
142
  </IconButton>
143
143
  </template>
144
144
 
@@ -147,14 +147,14 @@ if (__SLIDEV_FEATURE_RECORD__)
147
147
  title="Show info"
148
148
  @click="showInfoDialog = !showInfoDialog"
149
149
  >
150
- <carbon:information />
150
+ <div class="i-carbon:information" />
151
151
  </IconButton>
152
152
 
153
153
  <template v-if="!isPresenter && !isEmbedded">
154
154
  <MenuButton>
155
155
  <template #button>
156
156
  <IconButton title="Adjust settings">
157
- <carbon:settings-adjust />
157
+ <div class="i-carbon:settings-adjust" />
158
158
  </IconButton>
159
159
  </template>
160
160
  <template #menu>
@@ -159,7 +159,7 @@ watchEffect(() => {
159
159
  </Transition>
160
160
  <div v-if="showOverview" class="fixed top-4 right-4 z-20 text-gray-400 flex flex-col items-center gap-2">
161
161
  <IconButton title="Close" class="text-2xl" @click="close">
162
- <carbon:close />
162
+ <div class="i-carbon:close" />
163
163
  </IconButton>
164
164
  <IconButton
165
165
  v-if="__SLIDEV_FEATURE_PRESENTER__"
@@ -170,7 +170,7 @@ watchEffect(() => {
170
170
  tab-index="-1"
171
171
  class="text-2xl"
172
172
  >
173
- <carbon:list-boxes />
173
+ <div class="i-carbon:list-boxes" />
174
174
  </IconButton>
175
175
  </div>
176
176
  </template>
@@ -41,7 +41,7 @@ onMounted(() => {
41
41
  title="Toggle camera view"
42
42
  @click="toggleAvatar"
43
43
  >
44
- <carbon:user-avatar />
44
+ <div class="i-carbon:user-avatar" />
45
45
  </IconButton>
46
46
 
47
47
  <IconButton
@@ -49,13 +49,13 @@ onMounted(() => {
49
49
  :title="recording ? 'Stop record video' : 'Record video'"
50
50
  @click="toggleRecording"
51
51
  >
52
- <carbon:stop-outline v-if="recording" />
53
- <carbon:video v-else />
52
+ <div v-if="recording" class="i-carbon:stop-outline" />
53
+ <div v-else class="i-carbon:video" />
54
54
  </IconButton>
55
55
  <MenuButton :disabled="recording">
56
56
  <template #button>
57
57
  <IconButton title="Select recording device" class="h-full !text-sm !px-0 aspect-initial">
58
- <carbon:chevron-up class="opacity-50" />
58
+ <div class="i-carbon:chevron-up opacity-50" />
59
59
  </IconButton>
60
60
  </template>
61
61
  <template #menu>
@@ -34,7 +34,7 @@ async function start() {
34
34
  <template>
35
35
  <Modal v-model="value" class="px-6 py-4 recording-dialog flex flex-col gap-2">
36
36
  <div class="flex gap-2 text-xl">
37
- <carbon:video class="my-auto" />Recording
37
+ <div class="i-carbon:video my-auto" />Recording
38
38
  </div>
39
39
  <div class="grid grid-cols-2 gap-4">
40
40
  <div class="flex flex-col gap-2 py-2">
@@ -34,8 +34,10 @@ const value = useVModel(props, 'modelValue', emit, { passive: true })
34
34
  :class="{ active: value === item.value }"
35
35
  @click="() => { value = item.value; item.onClick?.() }"
36
36
  >
37
- <carbon:checkmark class="text-green-500" :class="{ 'opacity-0': value !== item.value }" />
38
- {{ item.display || item.value }}
37
+ <div class="i-carbon:checkmark text-green-500 mya" :class="{ 'opacity-0': value !== item.value }" />
38
+ <div :class="{ 'opacity-50': value !== item.value }">
39
+ {{ item.display || item.value }}
40
+ </div>
39
41
  </div>
40
42
  </div>
41
43
  </div>
@@ -47,7 +49,7 @@ const value = useVModel(props, 'modelValue', emit, { passive: true })
47
49
  }
48
50
 
49
51
  .item {
50
- @apply flex rounded whitespace-nowrap py-1 px-4 cursor-default hover:bg-gray-400 hover:bg-opacity-10;
52
+ @apply flex rounded whitespace-nowrap py-1 gap-1 px-2 cursor-default hover:bg-gray-400 hover:bg-opacity-10;
51
53
 
52
54
  svg {
53
55
  @apply mr-1 -ml-2 my-auto;
@@ -143,13 +143,13 @@ throttledWatch(
143
143
  title="Switch to content tab" :class="tab === 'content' ? 'text-primary' : ''"
144
144
  @click="switchTab('content')"
145
145
  >
146
- <carbon:account />
146
+ <div class="i-carbon:account" />
147
147
  </IconButton>
148
148
  <IconButton
149
149
  title="Switch to notes tab" :class="tab === 'note' ? 'text-primary' : ''"
150
150
  @click="switchTab('note')"
151
151
  >
152
- <carbon:align-box-bottom-right />
152
+ <div class="i-carbon:align-box-bottom-right" />
153
153
  </IconButton>
154
154
  </div>
155
155
  <span class="text-2xl pt-1">
@@ -158,17 +158,17 @@ throttledWatch(
158
158
  <div class="flex-auto" />
159
159
  <template v-if="resize">
160
160
  <IconButton v-if="vertical" title="Dock to right" @click="vertical = false">
161
- <carbon:open-panel-right />
161
+ <div class="i-carbon:open-panel-right" />
162
162
  </IconButton>
163
163
  <IconButton v-else title="Dock to bottom" @click="vertical = true">
164
- <carbon:open-panel-bottom />
164
+ <div class="i-carbon:open-panel-bottom" />
165
165
  </IconButton>
166
166
  </template>
167
167
  <IconButton title="Open in editor" @click="openInEditor()">
168
- <carbon:launch />
168
+ <div class="i-carbon:launch" />
169
169
  </IconButton>
170
170
  <IconButton title="Close" @click="close">
171
- <carbon:close />
171
+ <div class="i-carbon:close" />
172
172
  </IconButton>
173
173
  </div>
174
174
  <div class="relative overflow-hidden rounded" style="background-color: var(--slidev-code-background)">
@@ -53,6 +53,7 @@ const style = computed<CSSProperties>(() => ({
53
53
  :data-slidev-no="props.route.no"
54
54
  :class="getSlideClass(route, ['slide', 'presenter'].includes(props.renderContext) ? '' : 'disable-view-transition')"
55
55
  :style="style"
56
+ :lang="props.route.meta.slide.frontmatter.lang"
56
57
  >
57
58
  <SlideBottom />
58
59
  <component :is="props.route.component" />
package/logic/utils.ts CHANGED
@@ -1,27 +1,4 @@
1
1
  import { parseRangeString } from '@slidev/parser/core'
2
- import { useTimestamp } from '@vueuse/core'
3
- import { computed, ref } from 'vue'
4
-
5
- export function useTimer() {
6
- const tsStart = ref(Date.now())
7
- const now = useTimestamp({
8
- interval: 1000,
9
- })
10
- const timer = computed(() => {
11
- const passed = (now.value - tsStart.value) / 1000
12
- const sec = Math.floor(passed % 60).toString().padStart(2, '0')
13
- const min = Math.floor(passed / 60).toString().padStart(2, '0')
14
- return `${min}:${sec}`
15
- })
16
- function resetTimer() {
17
- tsStart.value = now.value
18
- }
19
-
20
- return {
21
- timer,
22
- resetTimer,
23
- }
24
- }
25
2
 
26
3
  export function makeId(length = 5) {
27
4
  const result = []
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@slidev/client",
3
3
  "type": "module",
4
- "version": "0.50.0-beta.6",
4
+ "version": "0.50.0-beta.8",
5
5
  "description": "Presentation slides for developers",
6
6
  "author": "antfu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -29,40 +29,40 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@antfu/utils": "^0.7.10",
32
- "@iconify-json/carbon": "^1.2.3",
32
+ "@iconify-json/carbon": "^1.2.4",
33
33
  "@iconify-json/ph": "^1.2.1",
34
34
  "@iconify-json/svg-spinners": "^1.2.1",
35
- "@shikijs/monaco": "^1.22.1",
36
- "@shikijs/vitepress-twoslash": "^1.22.1",
35
+ "@shikijs/monaco": "^1.24.0",
36
+ "@shikijs/vitepress-twoslash": "^1.24.0",
37
37
  "@slidev/rough-notation": "^0.1.0",
38
38
  "@typescript/ata": "^0.9.7",
39
- "@unhead/vue": "^1.11.10",
40
- "@unocss/reset": "^0.63.6",
41
- "@vueuse/core": "^11.1.0",
42
- "@vueuse/math": "^11.1.0",
39
+ "@unhead/vue": "^1.11.13",
40
+ "@unocss/reset": "^0.65.0-beta.2",
41
+ "@vueuse/core": "^12.0.0",
42
+ "@vueuse/math": "^12.0.0",
43
43
  "@vueuse/motion": "^2.2.6",
44
- "drauu": "^0.4.1",
44
+ "drauu": "^0.4.2",
45
45
  "file-saver": "^2.0.5",
46
46
  "floating-vue": "^5.2.2",
47
47
  "fuse.js": "^7.0.0",
48
48
  "html-to-image": "^1.11.11",
49
49
  "katex": "^0.16.11",
50
50
  "lz-string": "^1.5.0",
51
- "mermaid": "^11.3.0",
52
- "monaco-editor": "^0.52.0",
53
- "prettier": "^3.3.3",
51
+ "mermaid": "^11.4.1",
52
+ "monaco-editor": "0.51.0",
53
+ "prettier": "^3.4.1",
54
54
  "recordrtc": "^5.6.2",
55
- "shiki": "^1.22.0",
55
+ "shiki": "^1.24.0",
56
56
  "shiki-magic-move": "^0.5.0",
57
- "typescript": "^5.6.3",
58
- "unocss": "^0.63.4",
59
- "vue": "^3.5.12",
60
- "vue-router": "^4.4.5",
61
- "yaml": "^2.6.0",
62
- "@slidev/types": "0.50.0-beta.6",
63
- "@slidev/parser": "0.50.0-beta.6"
57
+ "typescript": "5.6.3",
58
+ "unocss": "^0.65.0-beta.2",
59
+ "vue": "^3.5.13",
60
+ "vue-router": "^4.5.0",
61
+ "yaml": "^2.6.1",
62
+ "@slidev/parser": "0.50.0-beta.8",
63
+ "@slidev/types": "0.50.0-beta.8"
64
64
  },
65
65
  "devDependencies": {
66
- "vite": "^5.4.9"
66
+ "vite": "^6.0.1"
67
67
  }
68
68
  }
package/pages/entry.vue CHANGED
@@ -1,16 +1,16 @@
1
1
  <template>
2
2
  <div class="h-full w-full flex items-center justify-center gap-5 lt-md:flex-col">
3
3
  <RouterLink to="/" class="page-link">
4
- <carbon:presentation-file /> Slides
4
+ <div class="i-carbon:presentation-file" /> Slides
5
5
  </RouterLink>
6
6
  <RouterLink to="/presenter" class="page-link">
7
- <carbon:user-speaker /> Presenter
7
+ <div class="i-carbon:user-speaker" /> Presenter
8
8
  </RouterLink>
9
9
  <RouterLink to="/notes" class="page-link">
10
- <carbon:catalog /> Notes
10
+ <div class="i-carbon:catalog" /> Notes
11
11
  </RouterLink>
12
12
  <RouterLink to="/overview" class="page-link">
13
- <carbon:list-boxes /> Overview
13
+ <div class="i-carbon:list-boxes" /> Overview
14
14
  </RouterLink>
15
15
  </div>
16
16
  </template>
package/pages/notes.vue CHANGED
@@ -67,14 +67,14 @@ const clicksContext = computed(() => {
67
67
  <div class="flex-none border-t border-main">
68
68
  <div class="flex gap-1 items-center px-6 py-3">
69
69
  <IconButton :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
70
- <carbon:minimize v-if="isFullscreen" />
71
- <carbon:maximize v-else />
70
+ <div v-if="isFullscreen" class="i-carbon:minimize" />
71
+ <div v-else class="i-carbon:maximize" />
72
72
  </IconButton>
73
73
  <IconButton title="Increase font size" @click="increaseFontSize">
74
- <carbon:zoom-in />
74
+ <div class="i-carbon:zoom-in" />
75
75
  </IconButton>
76
76
  <IconButton title="Decrease font size" @click="decreaseFontSize">
77
- <carbon:zoom-out />
77
+ <div class="i-carbon:zoom-out" />
78
78
  </IconButton>
79
79
  <div class="flex-auto" />
80
80
  <div class="p2 text-center">
@@ -183,7 +183,7 @@ onMounted(() => {
183
183
  title="Play in new tab"
184
184
  @click="openSlideInNewTab(getSlidePath(route, false))"
185
185
  >
186
- <carbon:presentation-file />
186
+ <div class="i-carbon:presentation-file" />
187
187
  </IconButton>
188
188
  <IconButton
189
189
  v-if="__DEV__ && route.meta?.slide"
@@ -191,7 +191,7 @@ onMounted(() => {
191
191
  title="Open in editor"
192
192
  @click="openInEditor(`${route.meta.slide.filepath}:${route.meta.slide.start}`)"
193
193
  >
194
- <carbon:cics-program />
194
+ <div class="i-carbon:cics-program" />
195
195
  </IconButton>
196
196
  </div>
197
197
  <div class="flex flex-col gap-2 my5" :style="{ width: `${cardWidth}px` }">
@@ -228,7 +228,7 @@ onMounted(() => {
228
228
  :class="edittingNote === route.no ? 'important:op0' : ''"
229
229
  @click="edittingNote = route.no"
230
230
  >
231
- <carbon:pen />
231
+ <div class="i-carbon:pen" />
232
232
  </IconButton>
233
233
  </div>
234
234
  <NoteEditable
@@ -6,6 +6,7 @@ import { createFixedClicks } from '../composables/useClicks'
6
6
  import { useDrawings } from '../composables/useDrawings'
7
7
  import { useNav } from '../composables/useNav'
8
8
  import { useSwipeControls } from '../composables/useSwipeControls'
9
+ import { useTimer } from '../composables/useTimer'
9
10
  import { useWakeLock } from '../composables/useWakeLock'
10
11
  import { slidesTitle } from '../env'
11
12
  import ClicksSlider from '../internals/ClicksSlider.vue'
@@ -22,7 +23,6 @@ import SlidesShow from '../internals/SlidesShow.vue'
22
23
  import SlideWrapper from '../internals/SlideWrapper.vue'
23
24
  import { onContextMenu } from '../logic/contextMenu'
24
25
  import { registerShortcuts } from '../logic/shortcuts'
25
- import { useTimer } from '../logic/utils'
26
26
  import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showPresenterCursor } from '../state'
27
27
  import { sharedState } from '../state/shared'
28
28
 
@@ -49,7 +49,7 @@ useHead({ title: `Presenter - ${slidesTitle}` })
49
49
 
50
50
  const notesEditing = ref(false)
51
51
 
52
- const { timer, resetTimer } = useTimer()
52
+ const { timer, isTimerAvctive, resetTimer, toggleTimer } = useTimer()
53
53
 
54
54
  const clicksCtxMap = computed(() => slides.value.map(route => createFixedClicks(route)))
55
55
  const nextFrame = computed(() => {
@@ -167,33 +167,39 @@ onMounted(() => {
167
167
  />
168
168
  <div class="border-t border-main py-1 px-2 text-sm">
169
169
  <IconButton title="Increase font size" @click="increasePresenterFontSize">
170
- <carbon:zoom-in />
170
+ <div class="i-carbon:zoom-in" />
171
171
  </IconButton>
172
172
  <IconButton title="Decrease font size" @click="decreasePresenterFontSize">
173
- <carbon:zoom-out />
173
+ <div class="i-carbon:zoom-out" />
174
174
  </IconButton>
175
175
  <IconButton
176
176
  v-if="__DEV__"
177
177
  title="Edit Notes"
178
178
  @click="notesEditing = !notesEditing"
179
179
  >
180
- <carbon:edit />
180
+ <div class="i-carbon:edit" />
181
181
  </IconButton>
182
182
  </div>
183
183
  </div>
184
184
  <div class="grid-section bottom flex">
185
185
  <NavControls :persist="true" />
186
186
  <div flex-auto />
187
- <div
188
- class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
189
- opacity="50 hover:100"
190
- @click="resetTimer"
191
- >
192
- <carbon:time class="absolute" />
193
- <carbon:renew class="absolute opacity-0" />
194
- </div>
195
- <div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
196
- {{ timer }}
187
+ <div class="group flex items-center justify-center pl-4 select-none">
188
+ <div class="w-22px cursor-pointer">
189
+ <div class="i-carbon:time group-hover:hidden text-xl" />
190
+ <div class="group-not-hover:hidden flex flex-col items-center">
191
+ <div class="relative op-80 hover:op-100" @click="toggleTimer">
192
+ <div v-if="isTimerAvctive" class="i-carbon:pause text-lg" />
193
+ <div v-else class="i-carbon:play" />
194
+ </div>
195
+ <div class="op-80 hover:op-100" @click="resetTimer">
196
+ <div class="i-carbon:renew" />
197
+ </div>
198
+ </div>
199
+ </div>
200
+ <div class="text-2xl px-3 my-auto tabular-nums">
201
+ {{ timer }}
202
+ </div>
197
203
  </div>
198
204
  </div>
199
205
  <DrawingControls v-if="__SLIDEV_FEATURE_DRAWINGS__" />
@@ -215,13 +221,6 @@ onMounted(() => {
215
221
  --slidev-controls-foreground: current;
216
222
  }
217
223
 
218
- .timer-btn:hover > :first-child {
219
- opacity: 0;
220
- }
221
- .timer-btn:hover > :last-child {
222
- opacity: 1;
223
- }
224
-
225
224
  .grid-container {
226
225
  --uno: bg-gray/20;
227
226
  height: 100%;
@@ -3,22 +3,13 @@
3
3
  import type { ContextMenuItem } from '@slidev/types'
4
4
  import type { ComputedRef } from 'vue'
5
5
  import setups from '#slidev/setups/context-menu'
6
- import IconApps from '~icons/carbon/apps'
7
- import IconArrowDown from '~icons/carbon/arrow-down'
8
- import IconArrowLeft from '~icons/carbon/arrow-left'
9
- import IconArrowRight from '~icons/carbon/arrow-right'
10
- import IconArrowUp from '~icons/carbon/arrow-up'
11
- import IconMaximize from '~icons/carbon/maximize'
12
- import IconMinimize from '~icons/carbon/minimize'
13
- import IconPen from '~icons/carbon/pen'
14
- import IconPresentationFile from '~icons/carbon/presentation-file'
15
- import IconTextNotationToggle from '~icons/carbon/text-annotation-toggle'
16
- import IconUserSpeaker from '~icons/carbon/user-speaker'
17
6
  import { computed } from 'vue'
18
7
  import { useDrawings } from '../composables/useDrawings'
19
8
  import { useNav } from '../composables/useNav'
20
9
  import { fullscreen, showEditor, toggleOverview } from '../state'
21
10
 
11
+ // @unocss-include
12
+
22
13
  let items: ComputedRef<ContextMenuItem[]> | undefined
23
14
 
24
15
  export default () => {
@@ -51,60 +42,60 @@ export default () => {
51
42
  computed(() => [
52
43
  {
53
44
  small: true,
54
- icon: IconArrowLeft,
45
+ icon: 'i-carbon:arrow-left',
55
46
  label: 'Previous Click',
56
47
  action: prev,
57
48
  disabled: !hasPrev.value,
58
49
  },
59
50
  {
60
51
  small: true,
61
- icon: IconArrowRight,
52
+ icon: 'i-carbon:arrow-right',
62
53
  label: 'Next Click',
63
54
  action: next,
64
55
  disabled: !hasNext.value,
65
56
  },
66
57
  {
67
58
  small: true,
68
- icon: IconArrowUp,
59
+ icon: 'i-carbon:arrow-up',
69
60
  label: 'Previous Slide',
70
61
  action: prevSlide,
71
62
  disabled: currentPage.value <= 1,
72
63
  },
73
64
  {
74
65
  small: true,
75
- icon: IconArrowDown,
66
+ icon: 'i-carbon:arrow-down',
76
67
  label: 'Next Slide',
77
68
  action: nextSlide,
78
69
  disabled: currentPage.value >= total.value,
79
70
  },
80
71
  'separator',
81
72
  {
82
- icon: IconTextNotationToggle,
73
+ icon: 'i-carbon:text-annotation-toggle', // IconTextNotationToggle,
83
74
  label: showEditor.value ? 'Hide editor' : 'Show editor',
84
75
  action: () => (showEditor.value = !showEditor.value),
85
76
  },
86
77
  {
87
- icon: IconPen,
78
+ icon: 'i-carbon:pen',
88
79
  label: drawingEnabled.value ? 'Hide drawing toolbar' : 'Show drawing toolbar',
89
80
  action: () => (drawingEnabled.value = !drawingEnabled.value),
90
81
  },
91
82
  {
92
- icon: IconApps,
83
+ icon: 'i-carbon:apps',
93
84
  label: 'Show slide overview',
94
85
  action: toggleOverview,
95
86
  },
96
87
  isPresenter.value && {
97
- icon: IconPresentationFile,
88
+ icon: 'i-carbon:presentation-file',
98
89
  label: 'Exit Presenter Mode',
99
90
  action: exitPresenter,
100
91
  },
101
92
  __SLIDEV_FEATURE_PRESENTER__ && isPresenterAvailable.value && {
102
- icon: IconUserSpeaker,
93
+ icon: 'i-carbon:user-speaker',
103
94
  label: 'Enter Presenter Mode',
104
95
  action: enterPresenter,
105
96
  },
106
97
  !isEmbedded.value && {
107
- icon: isFullscreen.value ? IconMinimize : IconMaximize,
98
+ icon: isFullscreen.value ? 'i-carbon:minimize' : 'i-carbon:maximize',
108
99
  label: isFullscreen.value ? 'Close fullscreen' : 'Enter fullscreen',
109
100
  action: toggleFullscreen,
110
101
  },
package/setup/monaco.ts CHANGED
@@ -6,22 +6,19 @@ import { setupTypeAcquisition } from '@typescript/ata'
6
6
  import * as monaco from 'monaco-editor'
7
7
 
8
8
  import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
9
+ // @ts-expect-error missing types
10
+ import { StandaloneServices } from 'monaco-editor/esm/vs/editor/standalone/browser/standaloneServices'
9
11
  import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
10
12
  import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
11
13
  import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
12
14
  import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
13
-
14
- import ts from 'typescript'
15
-
16
- import { watchEffect } from 'vue'
17
-
18
15
  // @ts-expect-error missing types
19
16
  import { ContextViewService } from 'monaco-editor/esm/vs/platform/contextview/browser/contextViewService'
20
-
21
17
  // @ts-expect-error missing types
22
18
  import { SyncDescriptor } from 'monaco-editor/esm/vs/platform/instantiation/common/descriptors'
23
- // @ts-expect-error missing types
24
- import { StandaloneServices } from 'monaco-editor/esm/vs/editor/standalone/browser/standaloneServices'
19
+
20
+ import ts from 'typescript'
21
+ import { watchEffect } from 'vue'
25
22
  import { isDark } from '../logic/dark'
26
23
 
27
24
  window.MonacoEnvironment = {
@@ -63,8 +63,7 @@ export default function setupShortcuts() {
63
63
  const baseShortcutNames = new Set(shortcuts.map(s => s.name))
64
64
 
65
65
  for (const setup of setups) {
66
- const result = setup(context, shortcuts)
67
- shortcuts = shortcuts.concat(result)
66
+ shortcuts = setup(context, shortcuts)
68
67
  }
69
68
 
70
69
  const remainingBaseShortcutNames = shortcuts.filter(s => s.name && baseShortcutNames.has(s.name))