fuc-new/doc/node_modules/vuepress-plugin-md-power/lib/client/components/CodeRepl.vue
hellcat 6adbc04d4b 1
2025-10-03 17:17:08 +08:00

243 lines
5.3 KiB
Vue

<script setup lang="ts">
import { defineAsyncComponent, shallowRef } from 'vue'
import { useCodeRepl } from '../composables/codeRepl.js'
import IconClose from './icons/IconClose.vue'
import IconConsole from './icons/IconConsole.vue'
import IconRun from './icons/IconRun.vue'
import Loading from './icons/Loading.vue'
defineProps<{
editable?: boolean
title?: string
}>()
const Editor = defineAsyncComponent(() => import('./CodeEditor.vue'))
const replEl = shallowRef<HTMLDivElement | null>(null)
const outputEl = shallowRef<HTMLDivElement | null>(null)
const {
onRunCode,
onCleanRun,
firstRun,
stderr,
stdout,
error,
loaded,
finished,
lang,
backendVersion,
} = useCodeRepl(replEl)
function runCode() {
onRunCode()
if (outputEl.value)
outputEl.value.scrollIntoView?.({ behavior: 'smooth', block: 'center' })
}
</script>
<template>
<div ref="replEl" class="code-repl">
<div class="code-repl-title">
<h4>{{ title }}</h4>
<span v-show="loaded && finished" class="icon-run" title="Run Code" @click="runCode">
<IconRun />
</span>
</div>
<Editor v-if="editable">
<slot />
</Editor>
<slot v-else />
<div ref="outputEl" class="code-repl-pin" />
<div v-if="!firstRun" class="code-repl-output">
<div class="output-head">
<IconConsole class="icon-console" />
<span class="title">console</span>
<span v-if="lang && backendVersion" class="output-version">
Running on: {{ lang }} <i>{{ backendVersion }}</i>
</span>
<IconClose class="icon-close" @click="onCleanRun" />
</div>
<div v-if="!loaded" class="output-content">
<Loading />
</div>
<div v-else class="output-content" :class="lang">
<p v-if="error" class="error">
{{ error }}
</p>
<div v-if="stderr.length" class="stderr">
<h4>Stderr:</h4>
<pre
v-for="(item, index) in stderr" :key="index"
:class="{ error: lang === 'rust' && item.startsWith('error') }"
>{{ item }}</pre>
</div>
<div v-if="stdout.length" class="stdout">
<h4 v-if="stderr.length">
Stdout:
</h4>
<pre v-for="(item, index) in stdout" :key="index">{{ item }}</pre>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.code-repl {
position: relative;
margin-bottom: 16px;
}
.code-repl :deep(div[class*="language-"]) {
margin: 0 -1.5rem;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.code-repl-output {
position: relative;
top: -20px;
padding-top: 6px;
margin: 0 -1.5rem;
background-color: var(--vp-code-block-bg);
transition: background-color var(--vp-t-color);
}
.code-repl-title {
display: flex;
align-items: center;
padding: 0 20px;
margin: 0 -1.5rem;
background-color: var(--vp-code-block-bg);
border-bottom: solid 1px var(--vp-c-divider);
transition: var(--vp-t-color);
transition-property: background, border;
}
@media (min-width: 640px) {
.code-repl-title {
margin: 0;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
.code-repl :deep(div[class*="language-"]) {
margin: 0;
}
.code-repl-output {
margin: 0;
border-bottom-right-radius: 6px;
border-bottom-left-radius: 6px;
}
}
.code-repl-title h4 {
flex: 1 2;
padding: 0 12px;
margin: 0;
font-size: 14px;
font-weight: 500;
line-height: 48px;
color: var(--vp-code-tab-active-text-color);
white-space: nowrap;
transition: color var(--vp-t-color);
}
.icon-run {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
font-size: 12px;
color: var(--vp-c-text-3);
cursor: pointer;
border: solid 1px var(--vp-c-text-3);
border-radius: 100%;
transition: var(--vp-t-color);
transition-property: color, border;
}
.icon-run:hover {
color: var(--vp-c-text-2);
border-color: var(--vp-c-text-2);
}
.code-repl-output .output-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 10px 4px 20px;
border-top: solid 2px var(--vp-c-divider);
transition: border-color var(--vp-t-color);
}
.output-head .title {
flex: 1 2;
margin-left: 10px;
font-size: 14px;
font-weight: 500;
}
.output-head .output-version {
font-size: 12px;
color: var(--vp-c-text-3);
transition: color var(--vp-t-color);
}
.output-head .icon-close {
width: 20px;
height: 20px;
margin-left: 20px;
color: var(--vp-c-text-3);
cursor: pointer;
transition: color var(--vp-t-color);
}
.output-head .icon-close:hover {
color: var(--vp-c-text-2);
}
.output-content {
padding: 12px 20px 24px;
overflow-x: auto;
}
.output-content h4 {
margin: 8px 0;
font-size: 16px;
}
.output-content pre {
margin: 0;
font-size: 14px;
line-height: 20px;
}
.output-content pre {
width: fit-content;
padding: 0 20px 0 0;
margin: 0;
overflow-x: initial;
}
.output-content .error,
.output-content .stderr pre,
.output-content.rust .stderr pre.error {
color: var(--vp-c-danger-1, #b8272c);
transition: color var(--vp-t-color);
}
.output-content.rust .stderr pre {
color: var(--vp-c-text-1);
}
.output-content .stderr + .stdout {
margin-top: 12px;
border-top: 1px solid var(--vp-c-divider);
transition: border-color var(--vp-t-color);
}
</style>