This commit is contained in:
lhx-666-cool
2025-04-25 17:03:25 +08:00
parent 326a2a007c
commit f6cb277519
1677 changed files with 1232721 additions and 0 deletions

9
web/src/App.vue Normal file
View File

@@ -0,0 +1,9 @@
<template>
<div class="flex flex-col h-screen">
<NavBar />
<router-view :key="$route.fullPath" />
</div>
</template>
<script setup>
import NavBar from './components/NavBar.vue';
</script>

BIN
web/src/assets/meow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 KiB

1
web/src/assets/run.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#FFFFFF"><path d="M320-200v-560l440 280-440 280Zm80-280Zm0 134 210-134-210-134v268Z"/></svg>

After

Width:  |  Height:  |  Size: 190 B

1
web/src/assets/save.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#FFFFFF"><path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z"/></svg>

After

Width:  |  Height:  |  Size: 388 B

1
web/src/assets/vue.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,43 @@
<script setup>
import { ref } from 'vue'
defineProps({
msg: String,
})
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div class="bg-black text-lg flex flex-row **:items-center p-4">
<div class="w-10 h-10 rounded-full overflow-hidden">
<img :src="MeowLogo" alt="Logo">
</div>
<div class="text-white ml-4 font-bold text-xl leading-10">
Meow Paste
</div>
</div>
</template>
<script setup>
import MeowLogo from '../assets/meow.png'
</script>

6
web/src/main.js Normal file
View File

@@ -0,0 +1,6 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')

209
web/src/pages/CodePage.vue Normal file
View File

@@ -0,0 +1,209 @@
<template>
<div class="bg-[#282c34] text-md flex justify-center items-center w-[100vw] flex-1 min-h-0">
<div class="w-3/4 h-3/4 bg-[#282c34] text-sm scale-133 flex">
<div class="w-3/4 h-full min-w-0 overflow-auto">
<div ref="editorContainer" class="w-full h-full">
</div>
</div>
<div class="flex-1 bg-stone-900 min-w-0 text-xs">
<div class="h-1/2 text-white flex flex-col">
<div class="bg-stone-700 p-1 px-2 flex items-center">
<div>
输入
</div>
<div class="flex-1 min-w-0">
</div>
<div class="h-4 w-4 mr-2" @click="handleRun(false)">
<img :src="saveIcon" alt="run">
</div>
<div class="h-4 w-4" @click="handleRun(true)">
<img :src="runIcon" alt="run">
</div>
</div>
<div class="flex-1 min-h-0 ">
<div class="w-full h-full overflow-hidden">
<textarea name="stdin" id="stdin" class="w-full h-full resize-none outline-none border-0 p-2 whitespace-nowrap"
placeholder="请输入测试样例" v-model="stdin"></textarea>
</div>
</div>
</div>
<div class="h-1/2 text-white flex flex-col">
<div class="bg-stone-700 p-1 px-2">
输出
</div>
<div class="flex-1 min-h-0 overflow-auto">
<div class="w-full h-full p-2">
<!-- {{ stdout === '' ? 'Empty' : stdout }} -->
<div v-show="time !== 0">
运行时间{{ time }} ms
</div>
{{ status !== 'completed' ? status : (stdout === '' ? 'Empty' : stdout) }}
<div v-show="stderr !== ''" class="text-red-500">
{{ stderr }}
</div>
<div v-show="log !== ''" class="text-red-500">
{{ log }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, defineProps } from 'vue'
import { EditorView } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { cpp } from '@codemirror/lang-cpp'
import { basicSetup } from 'codemirror'
import { languageServer } from 'codemirror-languageserver';
import { keymap } from '@codemirror/view'
import { indentMore, indentLess, insertTab } from "@codemirror/commands";
import { oneDark } from "@codemirror/theme-one-dark";
import runIcon from '../assets/run.svg'
import saveIcon from '../assets/save.svg'
import { useRouter } from 'vue-router'
const router = useRouter()
const props = defineProps({
id: {
type: String,
default: null,
}
});
const stdin = ref('')
const stdout = ref('')
const status = ref('completed')
const stderr = ref('')
const time = ref(0)
const log = ref('')
const serverUri = window.CONFIG.LSP_SERVER;
const backend = window.CONFIG.BACKEND;
const ls = languageServer({
serverUri,
rootUri: 'file:///main.cpp',
workspaceFolders: [],
documentUri: `file:///main.cpp`,
languageId: 'cpp'
});
const editorContainer = ref<HTMLElement | null>(null)
let view: EditorView
function getStatus(id: string) {
fetch(backend + `/api/pastes/${props.id}`)
.then(res => res.json())
.then(res => {
stdin.value = res.stdin
status.value = res.status
stdout.value = res.stdout
stderr.value = res.stderr
time.value = res.execution_time_ms
log.value = res.compile_log
})
.catch(err => {
console.log(err)
})
}
onMounted(() => {
const oldCode = localStorage.getItem('code')
stdin.value = localStorage.getItem('stdin') || ''
const state = EditorState.create({
doc: oldCode || `#include <bits/stdc++.h>\n\nint main() {\n\n}`,
extensions: [
basicSetup,
cpp(),
ls,
keymap.of([
{ key: "Tab", run: indentMore },
{ key: "Shift-Tab", run: indentLess },
// 如果需要在行中插入真实 Tab
{ key: "Mod-Tab", run: insertTab },
]),
oneDark,
]
})
view = new EditorView({
state,
parent: editorContainer.value as HTMLElement
})
if (props.id !== null) {
fetch(backend + `/api/pastes/${props.id}`)
.then(res => res.json())
.then(res => {
view.dispatch({
changes: {
from: 0,
to: view.state.doc.length,
insert: res.code
}
})
stdin.value = res.stdin
status.value = res.status
stdout.value = res.stdout
stderr.value = res.stderr
time.value = res.execution_time_ms
log.value = res.compile_log
if (status.value === 'pending' || status.value === 'running') {
let timer = setInterval(() => {
getStatus(props.id)
if (status.value !== 'pending' && status.value !== 'running') {
clearInterval(timer)
}
}, 1000)
}
})
.catch(err => {
console.log(err)
})
}
})
const handleRun = (isRun: boolean) => {
console.log('run')
fetch(backend + '/api/pastes', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
code: view.state.doc.toString(),
stdin: isRun ? stdin.value : '',
language: 'c++20',
run: isRun
})
})
.then(res => res.json())
.then(res => {
if (res.message === 'Created') {
const pasteid = res.paste_id;
router.push({
name: 'code',
params: {
id: pasteid
}
})
}
})
.catch(err => {
console.log(err)
})
}
setInterval(() => {
localStorage.setItem('code', view.state.doc.toString())
localStorage.setItem('stdin', stdin.value)
}, 1000)
</script>

21
web/src/router/index.js Normal file
View File

@@ -0,0 +1,21 @@
import {
createRouter,
createWebHistory
} from 'vue-router'
import CodePage from '../pages/CodePage.vue'
const routes = [{
path: '/code/:id?',
name: 'code',
component: CodePage,
props: true
}, {
path: '/',
redirect: '/code',
}]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router

25
web/src/style.css Normal file
View File

@@ -0,0 +1,25 @@
@import "tailwindcss";
/* 整个滚动条 */
::-webkit-scrollbar {
width: 4px; /* 滚动条宽度 */
height: 4px; /* 水平滚动条高度 */
}
/* 滚动条轨道 */
::-webkit-scrollbar-track {
background: #1c1917; /* 轨道颜色 */
border-radius: 10px; /* 圆角 */
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
background: #888; /* 滑块颜色 */
border-radius: 10px; /* 圆角 */
}
/* 鼠标悬停在滑块上 */
::-webkit-scrollbar-thumb:hover {
background: #555; /* 悬停时颜色变深 */
cursor: pointer;
}