11<template >
2- <div class =" interactive-code border border -gray-200 dark:border-gray-700 rounded-lg overflow-hidden my-4" >
3- <div class =" border-b border-gray-200 dark:border-gray-700" >
4- <div class =" px-4 py-2 flex justify-between items-center text-xs font-semibold bg-transparent dark:bg-[#1e1e1e]"
2+ <div class =" interactive-code border-gray-200 dark:border-gray-700 my-4" >
3+ <div class =" border border-gray-200 dark:border-gray-700 rounded-t-lg overflow-hidden sticky top-[64px] z-10 " >
4+ <div class =" px-4 py-2 flex justify-between items-center text-xs font-semibold bg-gray-50 dark:bg-[#1e1e1e]"
55 :class =" [colorTheme.text]" >
66 <span class =" text-sm" >{{ computedTitle }}</span >
77 <div class =" flex items-center gap-1" >
8- <button @click =" copyCode"
9- class =" text-current px-2 py-1.5 rounded-md text-xs cursor-pointer transition-all duration-200 flex items-center gap-1"
10- :class =" [colorTheme.hover]" title =" 复制代码" >
11- <CopyIcon v-if =" !copySuccess" :size =" 14" />
12- <CheckIcon v-else :size =" 14" />
13- <span class =" text-xs" >{{ copySuccess ? '已复制' : '复制' }}</span >
14- </button >
15- <button v-if =" runnable" @click =" resetCode"
16- class =" text-current px-2 py-1.5 rounded-md text-xs cursor-pointer transition-all duration-200 flex items-center gap-1"
17- :class =" [colorTheme.hover]" title =" 重置代码" >
18- <RotateCcwIcon :size =" 14" />
19- <span class =" text-xs" >重置</span >
20- </button >
21- <button v-if =" runnable" @click =" () => customRunCode(lang)"
22- class =" text-current px-2 py-1.5 rounded-md text-xs cursor-pointer transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1"
23- :class =" [colorTheme.hover]" :disabled =" isRunning || (lang === 'py' && !pyodideReady)"
24- :title =" getButtonText(lang)" >
25- <LoaderIcon v-if =" isRunning || (lang === 'py' && !pyodideReady)" :size =" 14" class =" animate-spin" />
26- <PlayIcon v-else :size =" 14" />
27- <span class =" text-xs" >{{ getSimpleButtonText(lang) }}</span >
28- </button >
8+ <SimpleTooltip content =" 复制代码" >
9+ <button @click =" copyCode"
10+ class =" text-current px-2 py-1.5 rounded-md text-xs cursor-pointer transition-all duration-200 flex items-center gap-1"
11+ :class =" [colorTheme.hover]" >
12+ <CopyIcon v-if =" !copySuccess" :size =" 14" />
13+ <CheckIcon v-else :size =" 14" />
14+ <span class =" text-xs" >{{ copySuccess ? '已复制' : '复制' }}</span >
15+ </button >
16+ </SimpleTooltip >
17+ <SimpleTooltip v-if =" runnable" content =" 重置代码" >
18+ <button @click =" resetCode"
19+ class =" text-current px-2 py-1.5 rounded-md text-xs cursor-pointer transition-all duration-200 flex items-center gap-1"
20+ :class =" [colorTheme.hover]" >
21+ <RotateCcwIcon :size =" 14" />
22+ <span class =" text-xs" >重置</span >
23+ </button >
24+ </SimpleTooltip >
25+ <SimpleTooltip v-if =" runnable" :content =" `${getButtonText(lang)} (Ctrl/⌘+Enter)`" >
26+ <button @click =" handleRunCode"
27+ class =" text-current px-2 py-1.5 rounded-md text-xs cursor-pointer transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1"
28+ :class =" [colorTheme.hover]" :disabled =" isRunning || (lang === 'py' && !pyodideReady)" >
29+ <LoaderIcon v-if =" isRunning || (lang === 'py' && !pyodideReady)" :size =" 14" class =" animate-spin" />
30+ <PlayIcon v-else :size =" 14" />
31+ <span class =" text-xs" >{{ getSimpleButtonText(lang) }}</span >
32+ </button >
33+ </SimpleTooltip >
2934 </div >
3035 </div >
3136 </div >
3237
33- <div class =" flex flex-col" >
38+ <div class =" flex flex-col border border-gray-200 dark:border-gray-700 rounded-b-lg overflow-hidden border-t-0 " >
3439 <div class =" relative" :class =" { 'readonly-code': !runnable }" >
3540 <!-- 懒加载占位符 -->
3641 <div v-if =" !isInitialized"
6974import { ref , computed , nextTick , onMounted , onUnmounted , type Ref } from ' vue'
7075import { PlayIcon , LoaderIcon , RotateCcwIcon , CopyIcon , CheckIcon , XIcon } from ' lucide-vue-next'
7176import { useCodeEditor } from ' ../composables/useCodeEditor'
77+ import SimpleTooltip from ' ./SimpleTooltip.vue'
7278
7379interface Props {
7480 lang: string
@@ -114,8 +120,23 @@ const {
114120 copyCode,
115121 getButtonText,
116122 getSimpleButtonText,
123+ enableKeyboardShortcuts,
117124 destroy
118- } = useCodeEditor ({ runnable: props .runnable , onCodeChange })
125+ } = useCodeEditor ({
126+ runnable: props .runnable ,
127+ onCodeChange ,
128+ lang: props .lang ,
129+ customRunFunction : async (lang : string ) => {
130+ await runCode (lang )
131+ showOutput .value = true
132+ }
133+ })
134+
135+ // 本地运行函数(用于按钮点击)
136+ const handleRunCode = async () => {
137+ await runCode (props .lang )
138+ showOutput .value = true
139+ }
119140
120141// 计算属性
121142const computedTitle = computed ((): string => {
@@ -202,6 +223,9 @@ async function initializeLazyEditor(): Promise<void> {
202223 }
203224
204225 setupThemeObserver ()
226+
227+ // 设置键盘快捷键
228+ enableKeyboardShortcuts ()
205229}
206230
207231// 设置主题观察器
@@ -266,14 +290,17 @@ function setupIntersectionObserver(): void {
266290function resetCode(): void {
267291 const processedCode = processCode (props .code )
268292
269- // 先销毁现有编辑器
293+ // 先销毁现有编辑器(会自动清理快捷键)
270294 destroy ()
271295
272296 // 重新初始化编辑器
273297 initializeEditor (props .lang , processedCode , {
274298 maxHeight: ' 600px'
275299 })
276300
301+ // 重新设置键盘快捷键
302+ enableKeyboardShortcuts ()
303+
277304 output .value = ' '
278305 hasError .value = false
279306 showOutput .value = false
@@ -284,12 +311,7 @@ function closeOutput(): void {
284311 showOutput .value = false
285312}
286313
287- // 覆盖运行函数以显示输出
288- const originalRunCode = runCode
289- const customRunCode = async (lang : string ) => {
290- await originalRunCode (lang )
291- showOutput .value = true
292- }
314+
293315
294316onMounted (async (): Promise <void > => {
295317 await nextTick ()
@@ -302,7 +324,7 @@ onMounted(async (): Promise<void> => {
302324})
303325
304326onUnmounted ((): void => {
305- destroy ()
327+ destroy () // destroy 函数会自动清理快捷键
306328 if (intersectionObserver ) {
307329 intersectionObserver .disconnect ()
308330 intersectionObserver = null
0 commit comments