This commit is contained in:
2
.env
2
.env
@@ -1,4 +1,4 @@
|
||||
PUBLIC_JUDGE0API_URL=https://judge0api.xuyue.cc
|
||||
PUBLIC_MAXKB_URL=https://maxkb.xuyue.cc/chat/api/embed?protocol=https&host=maxkb.xuyue.cc&token=2e801f7d6efdcc99
|
||||
PUBLIC_CODEAPI_URL=https://code.xuyue.cc/api
|
||||
PUBLIC_CODEAPI_URL=http://localhost:8080
|
||||
PUBLIC_PYVIZ_URL=https://pyviz.xuyue.cc
|
||||
78
package-lock.json
generated
78
package-lock.json
generated
@@ -17,6 +17,9 @@
|
||||
"copy-text-to-clipboard": "^3.2.1",
|
||||
"fflate": "^0.8.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"highlight.js": "^11.11.1",
|
||||
"marked": "^16.3.0",
|
||||
"marked-highlight": "^2.2.2",
|
||||
"naive-ui": "^2.43.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"query-string": "^9.3.0",
|
||||
@@ -441,6 +444,7 @@
|
||||
"integrity": "sha512-1/yyJJfZo4hqMsL3WQQmMDYFp0L/znHqjHrYE6NKsiKhkBEwEwSVMk1M5QoRu2EcRL1acW5AJf7WJyKFfPZ//Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rspack/core": "1.5.4",
|
||||
"@rspack/lite-tapable": "~1.0.1",
|
||||
@@ -667,6 +671,7 @@
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
@@ -1077,6 +1082,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1103,6 +1109,7 @@
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -1203,6 +1210,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001663",
|
||||
"electron-to-chromium": "^1.5.28",
|
||||
@@ -1294,6 +1302,7 @@
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@@ -1376,6 +1385,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz",
|
||||
"integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/hash": "~0.8.0",
|
||||
"csstype": "~3.0.5"
|
||||
@@ -1397,6 +1407,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
@@ -1812,9 +1823,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz",
|
||||
"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
@@ -1909,6 +1921,28 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz",
|
||||
"integrity": "sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/marked-highlight": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.2.2.tgz",
|
||||
"integrity": "sha512-KlHOP31DatbtPPXPaI8nx1KTrG3EW0Z5zewCwpUj65swbtKOTStteK3sNAjBqV75Pgo3fNEVNHeptg18mDuWgw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"marked": ">=4 <17"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -2308,6 +2342,7 @@
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -2383,6 +2418,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
|
||||
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.21",
|
||||
"@vue/compiler-sfc": "3.5.21",
|
||||
@@ -2480,6 +2516,7 @@
|
||||
"integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
@@ -2887,6 +2924,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.5.7.tgz",
|
||||
"integrity": "sha512-1/yyJJfZo4hqMsL3WQQmMDYFp0L/znHqjHrYE6NKsiKhkBEwEwSVMk1M5QoRu2EcRL1acW5AJf7WJyKFfPZ//Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@rspack/core": "1.5.4",
|
||||
"@rspack/lite-tapable": "~1.0.1",
|
||||
@@ -3018,6 +3056,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
@@ -3373,7 +3412,8 @@
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"acorn-import-phases": {
|
||||
"version": "1.0.4",
|
||||
@@ -3387,6 +3427,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -3446,6 +3487,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.0.tgz",
|
||||
"integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001663",
|
||||
"electron-to-chromium": "^1.5.28",
|
||||
@@ -3499,6 +3541,7 @@
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@@ -3558,6 +3601,7 @@
|
||||
"version": "0.15.14",
|
||||
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz",
|
||||
"integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@emotion/hash": "~0.8.0",
|
||||
"csstype": "~3.0.5"
|
||||
@@ -3578,7 +3622,8 @@
|
||||
"date-fns": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww=="
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"peer": true
|
||||
},
|
||||
"date-fns-tz": {
|
||||
"version": "3.2.0",
|
||||
@@ -3843,9 +3888,9 @@
|
||||
}
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "11.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz",
|
||||
"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw=="
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="
|
||||
},
|
||||
"jest-worker": {
|
||||
"version": "27.5.1",
|
||||
@@ -3916,6 +3961,18 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz",
|
||||
"integrity": "sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==",
|
||||
"peer": true
|
||||
},
|
||||
"marked-highlight": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.2.2.tgz",
|
||||
"integrity": "sha512-KlHOP31DatbtPPXPaI8nx1KTrG3EW0Z5zewCwpUj65swbtKOTStteK3sNAjBqV75Pgo3fNEVNHeptg18mDuWgw==",
|
||||
"requires": {}
|
||||
},
|
||||
"math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -4165,7 +4222,8 @@
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"devOptional": true
|
||||
"devOptional": true,
|
||||
"peer": true
|
||||
},
|
||||
"undici-types": {
|
||||
"version": "7.8.0",
|
||||
@@ -4203,6 +4261,7 @@
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
|
||||
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.5.21",
|
||||
"@vue/compiler-sfc": "3.5.21",
|
||||
@@ -4267,6 +4326,7 @@
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz",
|
||||
"integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"copy-text-to-clipboard": "^3.2.1",
|
||||
"fflate": "^0.8.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"highlight.js": "^11.11.1",
|
||||
"marked": "^16.3.0",
|
||||
"marked-highlight": "^2.2.2",
|
||||
"naive-ui": "^2.43.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"query-string": "^9.3.0",
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import { computed, reactive } from "vue"
|
||||
import { Status } from "../types"
|
||||
import { output, status } from "./code"
|
||||
|
||||
export const analyse = reactive({
|
||||
line: -1,
|
||||
message: "",
|
||||
})
|
||||
|
||||
export const showAnalyse = computed(
|
||||
() => ![Status.Accepted, Status.NotStarted].includes(status.value),
|
||||
)
|
||||
|
||||
function findError(line: string, language = "python") {
|
||||
const python: any = {
|
||||
"EOFError: EOF when reading a line": "需要在输入框填写输入信息",
|
||||
"SyntaxError: invalid character in identifier":
|
||||
"可能是单词拼写错误,可能是括号、引号写成中文的了",
|
||||
"SyntaxError: invalid syntax": "语法错误,不合法的语法",
|
||||
"SyntaxError: EOL while scanning string literal":
|
||||
"可能是这一行最后一个符号是中文的,或者引号、括号不匹配",
|
||||
"NameError: name '(.*?)' is not defined": (name: string) =>
|
||||
`命名错误,${name} 不知道是什么东西`,
|
||||
"IndentationError: expected an indented block": "缩进错误:这一行需要缩进",
|
||||
'TypeError: can only concatenate str \\(not "(.*?)"\\) to str':
|
||||
"文字和数字不能相加",
|
||||
}
|
||||
const c: any = {}
|
||||
const regex = { c, python }[language]
|
||||
let message = ""
|
||||
for (let r in regex) {
|
||||
const err = line.match(r)
|
||||
if (err) {
|
||||
if (typeof regex[r] === "function") {
|
||||
message = regex[r](err[1])
|
||||
} else {
|
||||
message = regex[r]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
export function analyzeError() {
|
||||
const line = output.value.match(/File "script.py", line (\d+)/)
|
||||
if (line) {
|
||||
analyse.line = parseInt(line[1])
|
||||
}
|
||||
const lines = output.value.split("\n")
|
||||
const lastLine = lines[lines.length - 1]
|
||||
analyse.message = findError(lastLine)
|
||||
}
|
||||
150
src/composables/analysis.ts
Normal file
150
src/composables/analysis.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { computed, ref } from "vue"
|
||||
import { Status } from "../types"
|
||||
import { output, status, code } from "./code"
|
||||
|
||||
export const analysis = ref("")
|
||||
export const loading = ref(false)
|
||||
|
||||
export async function getAIAnalysis() {
|
||||
analysis.value = ""
|
||||
// 使用 streaming 流式方式 fetch /ai/analysis 接口,传入 code 和 error_info
|
||||
const baseUrl = import.meta.env.PUBLIC_CODEAPI_URL
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/ai`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
code: code.value,
|
||||
language: code.language,
|
||||
error_info: output.value,
|
||||
}),
|
||||
})
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) return
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ""
|
||||
let eventLines: string[] = []
|
||||
let currentEvent: string | null = null
|
||||
|
||||
const flushEvent = () => {
|
||||
if (eventLines.length === 0) return false
|
||||
const raw = eventLines.join("\n")
|
||||
eventLines = []
|
||||
const event = currentEvent ?? "message"
|
||||
currentEvent = null
|
||||
|
||||
if (!raw) return false
|
||||
|
||||
let payload: unknown
|
||||
try {
|
||||
payload = JSON.parse(raw)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("无法解析 SSE 数据", error, raw)
|
||||
return false
|
||||
}
|
||||
|
||||
const data = (payload as { data?: string }).data ?? ""
|
||||
const message = (payload as { message?: string }).message ?? ""
|
||||
|
||||
if (event === "chunk") {
|
||||
appendContent(data)
|
||||
return false
|
||||
}
|
||||
|
||||
if (event === "error") {
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
if (message) {
|
||||
appendContent(`\n[错误] ${message}`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (event === "done") {
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
appendContent(data || message)
|
||||
return false
|
||||
}
|
||||
|
||||
const processLine = (line: string) => {
|
||||
if (line === "") {
|
||||
return flushEvent()
|
||||
}
|
||||
|
||||
if (line.startsWith("event:")) {
|
||||
currentEvent = line.slice(6).trimStart()
|
||||
return false
|
||||
}
|
||||
|
||||
if (!line.startsWith("data:")) return false
|
||||
|
||||
let value = line.slice(5)
|
||||
if (value.startsWith(" ")) {
|
||||
value = value.slice(1)
|
||||
}
|
||||
eventLines.push(value)
|
||||
return false
|
||||
}
|
||||
|
||||
const processBuffer = (final = false) => {
|
||||
const lines = buffer.split("\n")
|
||||
if (!final) {
|
||||
buffer = lines.pop() ?? ""
|
||||
} else {
|
||||
buffer = ""
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
const shouldStop = processLine(line)
|
||||
if (shouldStop) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (final) {
|
||||
return processLine("")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
const appendContent = (segment: string) => {
|
||||
if (!segment) return
|
||||
analysis.value += segment
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = (await reader.read()) as ReadableStreamReadResult<
|
||||
Uint8Array<ArrayBuffer>
|
||||
>
|
||||
if (done) break
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
if (processBuffer()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (processBuffer(true)) {
|
||||
return
|
||||
}
|
||||
} finally {
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const showAnalysis = computed(
|
||||
() => ![Status.Accepted, Status.NotStarted].includes(status.value),
|
||||
)
|
||||
@@ -2,10 +2,16 @@
|
||||
import copyTextToClipboard from "copy-text-to-clipboard"
|
||||
import { useMessage } from "naive-ui"
|
||||
import { computed, watch, useTemplateRef } from "vue"
|
||||
import { marked } from "marked"
|
||||
// @ts-ignore
|
||||
import * as Sk from "skulpt"
|
||||
import CodeEditor from "../components/CodeEditor.vue"
|
||||
import { analyse, analyzeError, showAnalyse } from "../composables/analyse"
|
||||
import {
|
||||
analysis,
|
||||
loading,
|
||||
getAIAnalysis,
|
||||
showAnalysis,
|
||||
} from "../composables/analysis"
|
||||
import {
|
||||
clearInput,
|
||||
code,
|
||||
@@ -55,7 +61,7 @@ function runSkulptTurtle() {
|
||||
Sk.TurtleGraphics = {
|
||||
target: canvas,
|
||||
width: canvas.clientWidth,
|
||||
height: canvas.clientHeight
|
||||
height: canvas.clientHeight,
|
||||
}
|
||||
Sk.misceval
|
||||
.asyncToPromise(function () {
|
||||
@@ -134,26 +140,19 @@ watch(turtleRunId, () => runSkulptTurtle())
|
||||
<n-tag v-if="status === Status.Accepted" type="success">
|
||||
运行成功
|
||||
</n-tag>
|
||||
<n-tag v-if="showAnalyse" type="warning">运行失败</n-tag>
|
||||
<n-popover
|
||||
v-if="showAnalyse && code.language === 'python'"
|
||||
trigger="click"
|
||||
>
|
||||
<n-tag v-if="showAnalysis" type="warning">运行失败</n-tag>
|
||||
<n-popover v-if="showAnalysis" trigger="click">
|
||||
<template #trigger>
|
||||
<n-button quaternary type="error" @click="analyzeError">
|
||||
<n-button quaternary type="error" @click="getAIAnalysis">
|
||||
推测原因
|
||||
</n-button>
|
||||
</template>
|
||||
<template #header v-if="analyse.line > 0">
|
||||
错误在第
|
||||
<n-tag type="error">
|
||||
<b>{{ analyse.line }}</b>
|
||||
</n-tag>
|
||||
行
|
||||
</template>
|
||||
<span v-if="analyse.message">
|
||||
{{ analyse.message }}
|
||||
</span>
|
||||
<n-spin :show="loading">
|
||||
<div
|
||||
class="analysisPanel"
|
||||
v-html="marked.parse(analysis)"
|
||||
></div>
|
||||
</n-spin>
|
||||
</n-popover>
|
||||
</template>
|
||||
</CodeEditor>
|
||||
@@ -174,4 +173,11 @@ watch(turtleRunId, () => runSkulptTurtle())
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.analysisPanel {
|
||||
width: 400px;
|
||||
min-height: 60px;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
NTabPane,
|
||||
NTabs,
|
||||
NTag,
|
||||
NSpin,
|
||||
create,
|
||||
} from "naive-ui"
|
||||
import "normalize.css"
|
||||
@@ -45,6 +46,7 @@ const naive = create({
|
||||
NTabs,
|
||||
NTabPane,
|
||||
NDropdown,
|
||||
NSpin,
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ for i in range(4):
|
||||
|
||||
turtle.done()`
|
||||
|
||||
export const languageToId = {
|
||||
export const languageToId: { [key in string]: number } = {
|
||||
c: 50,
|
||||
cpp: 54,
|
||||
java: 62,
|
||||
|
||||
Reference in New Issue
Block a user