@tnotesjs/core 0.0.1 → 0.0.3

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.
Files changed (115) hide show
  1. package/README.md +84 -84
  2. package/dist/{chunk-CSSIRO3I.js → chunk-NI5B4S3T.js} +40 -3
  3. package/dist/cli/index.js +1 -1
  4. package/dist/vitepress/config/index.js +60 -17
  5. package/package.json +77 -77
  6. package/types/config.ts +61 -61
  7. package/types/index.ts +11 -11
  8. package/types/note.ts +33 -33
  9. package/vitepress/assets/icons/icon__check.svg +3 -3
  10. package/vitepress/assets/icons/icon__clipboard.svg +7 -7
  11. package/vitepress/assets/icons/icon__close.svg +1 -1
  12. package/vitepress/assets/icons/icon__collapse.svg +1 -1
  13. package/vitepress/assets/icons/icon__confirm.svg +1 -1
  14. package/vitepress/assets/icons/icon__copy.svg +4 -4
  15. package/vitepress/assets/icons/icon__focus.svg +1 -1
  16. package/vitepress/assets/icons/icon__fold.svg +3 -3
  17. package/vitepress/assets/icons/icon__folder.svg +1 -1
  18. package/vitepress/assets/icons/icon__fullscreen.svg +1 -1
  19. package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -1
  20. package/vitepress/assets/icons/icon__github.svg +3 -3
  21. package/vitepress/assets/icons/icon__mindmap.svg +1 -1
  22. package/vitepress/assets/icons/icon__next.svg +1 -1
  23. package/vitepress/assets/icons/icon__number_gray.svg +1 -1
  24. package/vitepress/assets/icons/icon__number_purple.svg +1 -1
  25. package/vitepress/assets/icons/icon__prev.svg +1 -1
  26. package/vitepress/assets/icons/icon__restore.svg +1 -1
  27. package/vitepress/assets/icons/icon__rotate.svg +4 -4
  28. package/vitepress/assets/icons/icon__search.svg +1 -1
  29. package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -1
  30. package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -1
  31. package/vitepress/assets/icons/icon__totop.svg +5 -5
  32. package/vitepress/assets/icons/icon__vscode.svg +5 -5
  33. package/vitepress/assets/icons/icon__zoom_fit.svg +1 -1
  34. package/vitepress/assets/icons/icon__zoom_in.svg +1 -1
  35. package/vitepress/assets/icons/icon__zoom_out.svg +1 -1
  36. package/vitepress/assets/icons/icon__zoom_reset.svg +1 -1
  37. package/vitepress/assets/icons/index.ts +38 -38
  38. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -20
  39. package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +373 -373
  40. package/vitepress/components/CodeBlockFullscreen/index.ts +115 -115
  41. package/vitepress/components/CodeBlockFullscreen/styles.css +64 -64
  42. package/vitepress/components/Discussions/Discussions.module.scss +32 -32
  43. package/vitepress/components/Discussions/Discussions.vue +211 -211
  44. package/vitepress/components/EnWordList/EnWordList.module.scss +124 -124
  45. package/vitepress/components/EnWordList/EnWordList.vue +543 -543
  46. package/vitepress/components/EnWordList/RightClickMenu.module.scss +22 -22
  47. package/vitepress/components/EnWordList/RightClickMenu.vue +66 -66
  48. package/vitepress/components/Footprints/Footprints.module.scss +93 -93
  49. package/vitepress/components/Footprints/Footprints.vue +377 -377
  50. package/vitepress/components/Layout/AboutModal.module.scss +233 -233
  51. package/vitepress/components/Layout/AboutModal.vue +105 -105
  52. package/vitepress/components/Layout/AboutPanel.vue +266 -266
  53. package/vitepress/components/Layout/ContentCollapse.vue +603 -603
  54. package/vitepress/components/Layout/CustomSidebar.vue +605 -605
  55. package/vitepress/components/Layout/DocBeforeControls.vue +139 -139
  56. package/vitepress/components/Layout/DocFooter.vue +225 -225
  57. package/vitepress/components/Layout/ImagePreview.module.scss +201 -201
  58. package/vitepress/components/Layout/ImagePreview.vue +281 -281
  59. package/vitepress/components/Layout/Layout.module.scss +661 -661
  60. package/vitepress/components/Layout/Layout.vue +542 -542
  61. package/vitepress/components/Layout/NoteStatus.vue +140 -140
  62. package/vitepress/components/Layout/SidebarItems.vue +263 -263
  63. package/vitepress/components/Layout/SidebarNavBefore.vue +92 -92
  64. package/vitepress/components/Layout/Swiper.vue +167 -167
  65. package/vitepress/components/Layout/ToggleFullContent.module.scss +11 -11
  66. package/vitepress/components/Layout/ToggleFullContent.vue +34 -34
  67. package/vitepress/components/Layout/ToggleSidebar.module.scss +11 -11
  68. package/vitepress/components/Layout/ToggleSidebar.vue +35 -35
  69. package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -88
  70. package/vitepress/components/Layout/composables/useNoteConfig.ts +121 -121
  71. package/vitepress/components/Layout/composables/useNoteSave.ts +173 -173
  72. package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -85
  73. package/vitepress/components/Layout/composables/useRedirect.ts +110 -110
  74. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -85
  75. package/vitepress/components/Layout/homeReadme.data.ts +124 -124
  76. package/vitepress/components/LoadingPage/LoadingPage.vue +192 -192
  77. package/vitepress/components/MarkMap/MarkMap.module.scss +159 -159
  78. package/vitepress/components/MarkMap/MarkMap.vue +404 -404
  79. package/vitepress/components/Mermaid/Mermaid.module.scss +275 -275
  80. package/vitepress/components/Mermaid/Mermaid.vue +364 -364
  81. package/vitepress/components/NotesTable/NotesTable.module.scss +77 -77
  82. package/vitepress/components/NotesTable/NotesTable.vue +98 -98
  83. package/vitepress/components/NotesTable/README.md +67 -67
  84. package/vitepress/components/Settings/Settings.module.scss +433 -433
  85. package/vitepress/components/Settings/Settings.vue +306 -306
  86. package/vitepress/components/SidebarCard/MindMapView.vue +483 -483
  87. package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -108
  88. package/vitepress/components/SidebarCard/SidebarCard.vue +948 -948
  89. package/vitepress/components/Tooltip/Tooltip.vue +70 -70
  90. package/vitepress/components/constants.ts +91 -91
  91. package/vitepress/components/notesConfig.data.ts +73 -73
  92. package/vitepress/components/sidebar.data.ts +59 -59
  93. package/vitepress/components/tnotes-config.data.ts +21 -21
  94. package/vitepress/components/utils.ts +26 -26
  95. package/vitepress/config/index.ts +141 -141
  96. package/vitepress/configs/constants.ts +26 -26
  97. package/vitepress/configs/head.config.ts +25 -25
  98. package/vitepress/configs/index.ts +9 -9
  99. package/vitepress/configs/markdown-it.d.ts +23 -23
  100. package/vitepress/configs/markdown.config.ts +366 -366
  101. package/vitepress/configs/theme.config.ts +108 -108
  102. package/vitepress/plugins/buildProgressPlugin.ts +466 -390
  103. package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -107
  104. package/vitepress/plugins/renameNotePlugin.ts +60 -60
  105. package/vitepress/plugins/updateConfigPlugin.ts +63 -63
  106. package/vitepress/theme/index.ts +95 -95
  107. package/vitepress/theme/styles/base.scss +50 -50
  108. package/vitepress/theme/styles/components/404.scss +31 -31
  109. package/vitepress/theme/styles/components/collapse.scss +175 -175
  110. package/vitepress/theme/styles/components/markmap.scss +101 -101
  111. package/vitepress/theme/styles/components/swiper.scss +255 -255
  112. package/vitepress/theme/styles/index.scss +25 -25
  113. package/vitepress/theme/styles/layout.scss +62 -62
  114. package/vitepress/theme/styles/utilities.scss +39 -39
  115. package/vitepress/theme/styles/vitepress-override.scss +25 -25
@@ -1,373 +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>
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>