@tnotesjs/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @tnotesjs/core might be problematic. Click here for more details.

Files changed (117) hide show
  1. package/README.md +105 -0
  2. package/dist/chunk-K3X5OP3N.js +1532 -0
  3. package/dist/cli/index.d.ts +2 -0
  4. package/dist/cli/index.js +4199 -0
  5. package/dist/index.d.ts +138 -0
  6. package/dist/index.js +9 -0
  7. package/package.json +74 -0
  8. package/types/config.ts +61 -0
  9. package/types/index.ts +11 -0
  10. package/types/note.ts +33 -0
  11. package/vitepress/assets/icons/icon__check.svg +3 -0
  12. package/vitepress/assets/icons/icon__clipboard.svg +8 -0
  13. package/vitepress/assets/icons/icon__close.svg +1 -0
  14. package/vitepress/assets/icons/icon__collapse.svg +1 -0
  15. package/vitepress/assets/icons/icon__confirm.svg +1 -0
  16. package/vitepress/assets/icons/icon__copy.svg +4 -0
  17. package/vitepress/assets/icons/icon__focus.svg +1 -0
  18. package/vitepress/assets/icons/icon__fold.svg +3 -0
  19. package/vitepress/assets/icons/icon__folder.svg +1 -0
  20. package/vitepress/assets/icons/icon__fullscreen.svg +1 -0
  21. package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -0
  22. package/vitepress/assets/icons/icon__github.svg +4 -0
  23. package/vitepress/assets/icons/icon__mindmap.svg +1 -0
  24. package/vitepress/assets/icons/icon__next.svg +1 -0
  25. package/vitepress/assets/icons/icon__number_gray.svg +1 -0
  26. package/vitepress/assets/icons/icon__number_purple.svg +1 -0
  27. package/vitepress/assets/icons/icon__prev.svg +1 -0
  28. package/vitepress/assets/icons/icon__restore.svg +1 -0
  29. package/vitepress/assets/icons/icon__rotate.svg +4 -0
  30. package/vitepress/assets/icons/icon__search.svg +1 -0
  31. package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -0
  32. package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -0
  33. package/vitepress/assets/icons/icon__totop.svg +6 -0
  34. package/vitepress/assets/icons/icon__vscode.svg +6 -0
  35. package/vitepress/assets/icons/icon__zoom_fit.svg +1 -0
  36. package/vitepress/assets/icons/icon__zoom_in.svg +1 -0
  37. package/vitepress/assets/icons/icon__zoom_out.svg +1 -0
  38. package/vitepress/assets/icons/icon__zoom_reset.svg +1 -0
  39. package/vitepress/assets/icons/index.ts +38 -0
  40. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -0
  41. package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +373 -0
  42. package/vitepress/components/CodeBlockFullscreen/index.ts +115 -0
  43. package/vitepress/components/CodeBlockFullscreen/styles.css +64 -0
  44. package/vitepress/components/Discussions/Discussions.module.scss +32 -0
  45. package/vitepress/components/Discussions/Discussions.vue +211 -0
  46. package/vitepress/components/EnWordList/EnWordList.module.scss +124 -0
  47. package/vitepress/components/EnWordList/EnWordList.vue +543 -0
  48. package/vitepress/components/EnWordList/RightClickMenu.module.scss +22 -0
  49. package/vitepress/components/EnWordList/RightClickMenu.vue +66 -0
  50. package/vitepress/components/Footprints/Footprints.module.scss +93 -0
  51. package/vitepress/components/Footprints/Footprints.vue +377 -0
  52. package/vitepress/components/Layout/AboutModal.module.scss +233 -0
  53. package/vitepress/components/Layout/AboutModal.vue +105 -0
  54. package/vitepress/components/Layout/AboutPanel.vue +266 -0
  55. package/vitepress/components/Layout/ContentCollapse.vue +603 -0
  56. package/vitepress/components/Layout/CustomSidebar.vue +605 -0
  57. package/vitepress/components/Layout/DocBeforeControls.vue +139 -0
  58. package/vitepress/components/Layout/DocFooter.vue +225 -0
  59. package/vitepress/components/Layout/ImagePreview.module.scss +201 -0
  60. package/vitepress/components/Layout/ImagePreview.vue +281 -0
  61. package/vitepress/components/Layout/Layout.module.scss +661 -0
  62. package/vitepress/components/Layout/Layout.vue +542 -0
  63. package/vitepress/components/Layout/NoteStatus.vue +140 -0
  64. package/vitepress/components/Layout/SidebarItems.vue +263 -0
  65. package/vitepress/components/Layout/SidebarNavBefore.vue +92 -0
  66. package/vitepress/components/Layout/Swiper.vue +167 -0
  67. package/vitepress/components/Layout/ToggleFullContent.module.scss +11 -0
  68. package/vitepress/components/Layout/ToggleFullContent.vue +34 -0
  69. package/vitepress/components/Layout/ToggleSidebar.module.scss +11 -0
  70. package/vitepress/components/Layout/ToggleSidebar.vue +35 -0
  71. package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -0
  72. package/vitepress/components/Layout/composables/useNoteConfig.ts +121 -0
  73. package/vitepress/components/Layout/composables/useNoteSave.ts +173 -0
  74. package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -0
  75. package/vitepress/components/Layout/composables/useRedirect.ts +110 -0
  76. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -0
  77. package/vitepress/components/Layout/homeReadme.data.ts +124 -0
  78. package/vitepress/components/LoadingPage/LoadingPage.vue +192 -0
  79. package/vitepress/components/MarkMap/MarkMap.module.scss +159 -0
  80. package/vitepress/components/MarkMap/MarkMap.vue +404 -0
  81. package/vitepress/components/Mermaid/Mermaid.module.scss +275 -0
  82. package/vitepress/components/Mermaid/Mermaid.vue +364 -0
  83. package/vitepress/components/NotesTable/NotesTable.module.scss +77 -0
  84. package/vitepress/components/NotesTable/NotesTable.vue +98 -0
  85. package/vitepress/components/NotesTable/README.md +67 -0
  86. package/vitepress/components/Settings/Settings.module.scss +433 -0
  87. package/vitepress/components/Settings/Settings.vue +306 -0
  88. package/vitepress/components/SidebarCard/MindMapView.vue +483 -0
  89. package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -0
  90. package/vitepress/components/SidebarCard/SidebarCard.vue +948 -0
  91. package/vitepress/components/Tooltip/Tooltip.vue +70 -0
  92. package/vitepress/components/constants.ts +91 -0
  93. package/vitepress/components/notesConfig.data.ts +73 -0
  94. package/vitepress/components/sidebar.data.ts +59 -0
  95. package/vitepress/components/tnotes-config.data.ts +21 -0
  96. package/vitepress/components/utils.ts +26 -0
  97. package/vitepress/config/index.ts +126 -0
  98. package/vitepress/configs/constants.ts +26 -0
  99. package/vitepress/configs/head.config.ts +25 -0
  100. package/vitepress/configs/index.ts +9 -0
  101. package/vitepress/configs/markdown-it.d.ts +23 -0
  102. package/vitepress/configs/markdown.config.ts +366 -0
  103. package/vitepress/configs/theme.config.ts +108 -0
  104. package/vitepress/plugins/buildProgressPlugin.ts +390 -0
  105. package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -0
  106. package/vitepress/plugins/renameNotePlugin.ts +60 -0
  107. package/vitepress/plugins/updateConfigPlugin.ts +63 -0
  108. package/vitepress/theme/index.ts +95 -0
  109. package/vitepress/theme/styles/base.scss +50 -0
  110. package/vitepress/theme/styles/components/404.scss +31 -0
  111. package/vitepress/theme/styles/components/collapse.scss +175 -0
  112. package/vitepress/theme/styles/components/markmap.scss +101 -0
  113. package/vitepress/theme/styles/components/swiper.scss +255 -0
  114. package/vitepress/theme/styles/index.scss +25 -0
  115. package/vitepress/theme/styles/layout.scss +62 -0
  116. package/vitepress/theme/styles/utilities.scss +39 -0
  117. package/vitepress/theme/styles/vitepress-override.scss +25 -0
@@ -0,0 +1,373 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <Transition name="fullscreen-fade">
4
+ <div
5
+ v-if="isFullscreen"
6
+ class="code-fullscreen-overlay"
7
+ :class="{ 'is-landscape': isLandscape }"
8
+ @click.self="closeFullscreen"
9
+ >
10
+ <div class="code-fullscreen-container">
11
+ <!-- 工具栏 -->
12
+ <div class="code-fullscreen-toolbar">
13
+ <div class="code-fullscreen-title">
14
+ <span v-if="language" class="language-tag">{{ language }}</span>
15
+ <span v-if="filename" class="filename">{{ filename }}</span>
16
+ </div>
17
+ <div class="code-fullscreen-actions">
18
+ <!-- 旋转按钮(仅移动端) -->
19
+ <button
20
+ class="action-btn rotate-btn mobile-only"
21
+ @click="toggleOrientation"
22
+ :title="isLandscape ? '切换为竖屏' : '切换为横屏'"
23
+ >
24
+ <img :src="icon__rotate" alt="旋转" />
25
+ </button>
26
+ <!-- 复制按钮 -->
27
+ <button
28
+ class="action-btn copy-btn"
29
+ @click="copyCode"
30
+ :title="copied ? '已复制' : '复制代码'"
31
+ >
32
+ <img v-if="!copied" :src="icon__copy" alt="复制" />
33
+ <img v-else :src="icon__check" alt="已复制" />
34
+ </button>
35
+ <!-- 关闭按钮 -->
36
+ <button
37
+ class="action-btn close-btn"
38
+ @click="closeFullscreen"
39
+ title="退出全屏 (ESC)"
40
+ >
41
+ <img :src="icon__close" alt="关闭" />
42
+ </button>
43
+ </div>
44
+ </div>
45
+
46
+ <!-- 代码内容 -->
47
+ <div class="code-fullscreen-content" ref="contentRef">
48
+ <div v-html="codeHtml"></div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </Transition>
53
+ </Teleport>
54
+ </template>
55
+
56
+ <script setup lang="ts">
57
+ import { ref, watch, onMounted, onUnmounted } from 'vue'
58
+ import { icon__copy, icon__check, icon__close, icon__rotate } from '../../assets/icons'
59
+
60
+ const ORIENTATION_KEY = 'code-fullscreen-orientation'
61
+
62
+ interface Props {
63
+ isFullscreen: boolean
64
+ codeHtml: string
65
+ language?: string
66
+ filename?: string
67
+ }
68
+
69
+ const props = defineProps<Props>()
70
+ const emit = defineEmits<{
71
+ 'update:isFullscreen': [value: boolean]
72
+ }>()
73
+
74
+ const contentRef = ref<HTMLElement | null>(null)
75
+ const copied = ref(false)
76
+ const isLandscape = ref(false)
77
+
78
+ // 检测是否为移动端
79
+ function isMobile() {
80
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
81
+ navigator.userAgent
82
+ )
83
+ }
84
+
85
+ // 切换横竖屏
86
+ function toggleOrientation() {
87
+ isLandscape.value = !isLandscape.value
88
+ // 保存用户偏好
89
+ if (typeof window !== 'undefined') {
90
+ localStorage.setItem(
91
+ ORIENTATION_KEY,
92
+ isLandscape.value ? 'landscape' : 'portrait'
93
+ )
94
+ }
95
+ }
96
+
97
+ function closeFullscreen() {
98
+ emit('update:isFullscreen', false)
99
+ }
100
+
101
+ function copyCode() {
102
+ if (!contentRef.value) return
103
+
104
+ const codeElement = contentRef.value.querySelector('pre code')
105
+ if (!codeElement) return
106
+
107
+ const text = codeElement.textContent || ''
108
+ navigator.clipboard.writeText(text).then(() => {
109
+ copied.value = true
110
+ setTimeout(() => {
111
+ copied.value = false
112
+ }, 2000)
113
+ })
114
+ }
115
+
116
+ function handleKeydown(e: KeyboardEvent) {
117
+ if (e.key === 'Escape' && props.isFullscreen) {
118
+ closeFullscreen()
119
+ }
120
+ }
121
+
122
+ watch(
123
+ () => props.isFullscreen,
124
+ (newVal) => {
125
+ if (newVal) {
126
+ document.body.style.overflow = 'hidden'
127
+ // 读取用户上次选择的方向
128
+ if (typeof window !== 'undefined') {
129
+ const savedOrientation = localStorage.getItem(ORIENTATION_KEY)
130
+ // 如果有保存的偏好,使用保存的值;否则默认为竖屏
131
+ if (savedOrientation) {
132
+ isLandscape.value = savedOrientation === 'landscape'
133
+ } else {
134
+ isLandscape.value = false
135
+ }
136
+ }
137
+ } else {
138
+ document.body.style.overflow = ''
139
+ }
140
+ }
141
+ )
142
+
143
+ onMounted(() => {
144
+ document.addEventListener('keydown', handleKeydown)
145
+ })
146
+
147
+ onUnmounted(() => {
148
+ document.removeEventListener('keydown', handleKeydown)
149
+ document.body.style.overflow = ''
150
+ })
151
+ </script>
152
+
153
+ <style scoped>
154
+ .code-fullscreen-overlay {
155
+ position: fixed;
156
+ top: 0;
157
+ left: 0;
158
+ right: 0;
159
+ bottom: 0;
160
+ width: 100vw;
161
+ height: 100vh;
162
+ background-color: rgba(0, 0, 0, 0.9);
163
+ z-index: 9999;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ }
168
+
169
+ .code-fullscreen-container {
170
+ width: 100%;
171
+ height: 100%;
172
+ background-color: var(--vp-c-bg);
173
+ display: flex;
174
+ flex-direction: column;
175
+ overflow: hidden;
176
+ transition: transform 0.3s ease;
177
+ }
178
+
179
+ /* 横屏模式 */
180
+ .code-fullscreen-overlay.is-landscape {
181
+ overflow: hidden;
182
+ }
183
+
184
+ .code-fullscreen-overlay.is-landscape .code-fullscreen-container {
185
+ position: absolute;
186
+ top: 0;
187
+ left: 0;
188
+ right: 0;
189
+ bottom: 0;
190
+ /* 关键:宽度取视口高度,高度取视口宽度 */
191
+ /* 这样旋转90度后,宽度(原高度100vh)会填满屏幕宽度 */
192
+ width: 100vh;
193
+ height: 100vw;
194
+ /* 以左上角为中心旋转,然后平移到正确位置 */
195
+ transform-origin: top left;
196
+ transform: rotate(90deg) translateY(-100%);
197
+ max-width: none;
198
+ max-height: none;
199
+ }
200
+
201
+ .code-fullscreen-toolbar {
202
+ display: flex;
203
+ align-items: center;
204
+ justify-content: space-between;
205
+ padding: 12px 20px;
206
+ background-color: var(--vp-c-bg-soft);
207
+ border-bottom: 1px solid var(--vp-c-divider);
208
+ flex-shrink: 0;
209
+ }
210
+
211
+ .code-fullscreen-title {
212
+ display: flex;
213
+ align-items: center;
214
+ gap: 12px;
215
+ font-size: 14px;
216
+ }
217
+
218
+ .language-tag {
219
+ display: inline-block;
220
+ padding: 4px 8px;
221
+ background-color: var(--vp-c-brand-soft);
222
+ color: var(--vp-c-brand-1);
223
+ border-radius: 4px;
224
+ font-family: var(--vp-font-family-mono);
225
+ font-size: 12px;
226
+ font-weight: 600;
227
+ text-transform: uppercase;
228
+ }
229
+
230
+ .filename {
231
+ color: var(--vp-c-text-2);
232
+ font-family: var(--vp-font-family-mono);
233
+ font-size: 13px;
234
+ }
235
+
236
+ .code-fullscreen-actions {
237
+ display: flex;
238
+ gap: 8px;
239
+ }
240
+
241
+ .action-btn {
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+ width: 36px;
246
+ height: 36px;
247
+ border: none;
248
+ background-color: transparent;
249
+ color: var(--vp-c-text-2);
250
+ border-radius: 6px;
251
+ cursor: pointer;
252
+ transition: all 0.2s;
253
+ }
254
+
255
+ .action-btn img {
256
+ width: 16px;
257
+ height: 16px;
258
+ filter: var(--vp-icon-filter, none);
259
+ }
260
+
261
+ .action-btn:hover {
262
+ background-color: var(--vp-c-default-soft);
263
+ color: var(--vp-c-text-1);
264
+ }
265
+
266
+ .close-btn img {
267
+ width: 20px;
268
+ height: 20px;
269
+ }
270
+
271
+ /* 旋转按钮仅移动端显示 */
272
+ .mobile-only {
273
+ display: none;
274
+ }
275
+
276
+ .code-fullscreen-content {
277
+ flex: 1;
278
+ overflow: auto;
279
+ padding: 20px;
280
+ background-color: var(--vp-code-block-bg);
281
+ }
282
+
283
+ .code-fullscreen-content :deep(pre) {
284
+ margin: 0 !important;
285
+ background-color: transparent !important;
286
+ padding: 0 !important;
287
+ }
288
+
289
+ .code-fullscreen-content :deep(code) {
290
+ font-size: 14px !important;
291
+ line-height: 1.7 !important;
292
+ }
293
+
294
+ /* 移动端优化 */
295
+ @media (max-width: 768px) {
296
+ .mobile-only {
297
+ display: flex;
298
+ }
299
+
300
+ .code-fullscreen-overlay {
301
+ padding: 0;
302
+ }
303
+
304
+ .code-fullscreen-container {
305
+ max-width: 100%;
306
+ max-height: 100vh;
307
+ border-radius: 0;
308
+ }
309
+
310
+ .code-fullscreen-toolbar {
311
+ padding: 10px 16px;
312
+ }
313
+
314
+ .code-fullscreen-title {
315
+ font-size: 13px;
316
+ }
317
+
318
+ .language-tag {
319
+ font-size: 11px;
320
+ padding: 3px 6px;
321
+ }
322
+
323
+ .filename {
324
+ font-size: 12px;
325
+ overflow: hidden;
326
+ text-overflow: ellipsis;
327
+ white-space: nowrap;
328
+ max-width: 120px;
329
+ }
330
+
331
+ .code-fullscreen-content {
332
+ padding: 16px;
333
+ }
334
+
335
+ .code-fullscreen-content :deep(code) {
336
+ font-size: 12px !important;
337
+ }
338
+
339
+ /* 横屏模式下的字体调整 */
340
+ .code-fullscreen-overlay.is-landscape .code-fullscreen-content :deep(code) {
341
+ font-size: 14px !important;
342
+ }
343
+ }
344
+
345
+ /* 过渡动画 */
346
+ .fullscreen-fade-enter-active,
347
+ .fullscreen-fade-leave-active {
348
+ transition: opacity 0.3s ease;
349
+ }
350
+
351
+ .fullscreen-fade-enter-from,
352
+ .fullscreen-fade-leave-to {
353
+ opacity: 0;
354
+ }
355
+
356
+ .fullscreen-fade-enter-active .code-fullscreen-container,
357
+ .fullscreen-fade-leave-active .code-fullscreen-container {
358
+ transition: transform 0.3s ease;
359
+ }
360
+
361
+ .fullscreen-fade-enter-from .code-fullscreen-container {
362
+ transform: scale(0.95);
363
+ }
364
+
365
+ .fullscreen-fade-leave-to .code-fullscreen-container {
366
+ transform: scale(0.95);
367
+ }
368
+
369
+ /* 暗色模式图标适配 */
370
+ .dark .action-btn img {
371
+ filter: invert(1);
372
+ }
373
+ </style>
@@ -0,0 +1,115 @@
1
+ import { onMounted, onUnmounted } from 'vue'
2
+ import { createApp } from 'vue'
3
+ import CodeBlockFullscreen from './CodeBlockFullscreen.vue'
4
+
5
+ export function useCodeBlockFullscreen() {
6
+ let fullscreenApp: any = null
7
+ let fullscreenContainer: HTMLElement | null = null
8
+
9
+ function addFullscreenButtons() {
10
+ // 找到所有代码块
11
+ const codeBlocks = document.querySelectorAll('div[class*="language-"]')
12
+
13
+ codeBlocks.forEach((block) => {
14
+ // 避免重复添加按钮
15
+ if (block.querySelector('.fullscreen-btn')) return
16
+
17
+ // 创建全屏按钮
18
+ const button = document.createElement('button')
19
+ button.className = 'fullscreen-btn'
20
+ button.title = '全屏查看代码'
21
+ button.innerHTML = `
22
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
23
+ <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>
24
+ </svg>
25
+ `
26
+
27
+ // 点击事件
28
+ button.addEventListener('click', () => {
29
+ openFullscreen(block as HTMLElement)
30
+ })
31
+
32
+ // 添加按钮到代码块
33
+ block.appendChild(button)
34
+ })
35
+ }
36
+
37
+ function openFullscreen(codeBlock: HTMLElement) {
38
+ // 获取代码内容
39
+ const pre = codeBlock.querySelector('pre')
40
+ if (!pre) return
41
+
42
+ const codeHtml = pre.outerHTML
43
+
44
+ // 提取语言
45
+ const classList = Array.from(codeBlock.classList)
46
+ const languageClass = classList.find((c) => c.startsWith('language-'))
47
+ const language = languageClass
48
+ ? languageClass.replace('language-', '').toUpperCase()
49
+ : undefined
50
+
51
+ // 提取文件名(如果有)
52
+ const filenameSpan = codeBlock.querySelector('.lang')
53
+ const filename = filenameSpan?.textContent?.trim()
54
+
55
+ // 创建全屏组件容器
56
+ if (!fullscreenContainer) {
57
+ fullscreenContainer = document.createElement('div')
58
+ document.body.appendChild(fullscreenContainer)
59
+ }
60
+
61
+ // 创建 Vue 应用
62
+ fullscreenApp = createApp(CodeBlockFullscreen, {
63
+ isFullscreen: true,
64
+ codeHtml,
65
+ language,
66
+ filename,
67
+ 'onUpdate:isFullscreen': (value: boolean) => {
68
+ if (!value) {
69
+ closeFullscreen()
70
+ }
71
+ },
72
+ })
73
+
74
+ fullscreenApp.mount(fullscreenContainer)
75
+ }
76
+
77
+ function closeFullscreen() {
78
+ if (fullscreenApp) {
79
+ fullscreenApp.unmount()
80
+ fullscreenApp = null
81
+ }
82
+ if (fullscreenContainer && fullscreenContainer.parentNode) {
83
+ fullscreenContainer.parentNode.removeChild(fullscreenContainer)
84
+ fullscreenContainer = null
85
+ }
86
+ }
87
+
88
+ function cleanup() {
89
+ closeFullscreen()
90
+ // 移除所有全屏按钮
91
+ document.querySelectorAll('.fullscreen-btn').forEach((btn) => btn.remove())
92
+ }
93
+
94
+ onMounted(() => {
95
+ // 初始化
96
+ setTimeout(() => {
97
+ addFullscreenButtons()
98
+ }, 100)
99
+
100
+ // 监听路由变化
101
+ const observer = new MutationObserver(() => {
102
+ addFullscreenButtons()
103
+ })
104
+
105
+ observer.observe(document.body, {
106
+ childList: true,
107
+ subtree: true,
108
+ })
109
+
110
+ onUnmounted(() => {
111
+ observer.disconnect()
112
+ cleanup()
113
+ })
114
+ })
115
+ }
@@ -0,0 +1,64 @@
1
+ /* 代码块全屏按钮样式 */
2
+ div[class*='language-'] {
3
+ position: relative;
4
+ }
5
+
6
+ .fullscreen-btn {
7
+ position: absolute;
8
+ top: 8px;
9
+ right: 8px;
10
+ z-index: 10;
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ width: 32px;
15
+ height: 32px;
16
+ padding: 0;
17
+ border: none;
18
+ background-color: var(--vp-code-block-bg);
19
+ color: var(--vp-c-text-2);
20
+ border-radius: 6px;
21
+ cursor: pointer;
22
+ opacity: 0;
23
+ transition: all 0.2s;
24
+ }
25
+
26
+ div[class*='language-']:hover .fullscreen-btn {
27
+ opacity: 1;
28
+ }
29
+
30
+ .fullscreen-btn:hover {
31
+ background-color: var(--vp-c-default-soft);
32
+ color: var(--vp-c-brand-1);
33
+ transform: scale(1.05);
34
+ }
35
+
36
+ .fullscreen-btn:active {
37
+ transform: scale(0.95);
38
+ }
39
+
40
+ .fullscreen-btn svg {
41
+ width: 16px;
42
+ height: 16px;
43
+ }
44
+
45
+ /* 移动端始终显示按钮 */
46
+ @media (max-width: 768px) {
47
+ .fullscreen-btn {
48
+ opacity: 0.8;
49
+ top: 6px;
50
+ right: 6px;
51
+ width: 28px;
52
+ height: 28px;
53
+ }
54
+
55
+ .fullscreen-btn svg {
56
+ width: 14px;
57
+ height: 14px;
58
+ }
59
+ }
60
+
61
+ div[class*='language-']:has(.copy) .fullscreen-btn {
62
+ /* 代码块已经有复制按钮,需要微调位置 */
63
+ right: 54px;
64
+ }
@@ -0,0 +1,32 @@
1
+ /* #region:same as vitepress theme */
2
+ .h2 {
3
+ margin: 48px 0 16px;
4
+ border-top: 1px solid var(--vp-c-divider);
5
+ padding-top: 24px;
6
+ letter-spacing: -0.02em;
7
+ line-height: 32px;
8
+ font-size: 1.2rem;
9
+ text-align: center;
10
+ }
11
+
12
+ /* #endregion:same as vitepress theme */
13
+
14
+ .discussionsLink {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: 0.5em;
18
+ color: var(--vp-c-brand-1);
19
+ text-decoration: none;
20
+ opacity: 0.9;
21
+ transition: opacity 0.25s;
22
+
23
+ &:hover {
24
+ opacity: 0.5;
25
+ }
26
+ }
27
+
28
+ .githubIcon {
29
+ width: 1em;
30
+ height: 1em;
31
+ flex-shrink: 0;
32
+ }