style(代码配色): 全站代码换成 Dracula 配色(亮色用官方浅色版 Alucard);编辑器、题面和教程的代码块、练一练、提交详情的颜色一模一样了
Deploy / deploy (push) Canceled after 0s

原来三套配色各管一块:编辑器是 smoothy / One Dark,正文代码块和练一练是仿 GitHub,
提交详情是 Naive 自带的,同一段 printf 在一课里就有两种颜色。现在静态代码也走编辑器
那套 lezer 解析器,颜色只在 shared/highlight.css 写一份,highlight.js 删掉。

填空题顺手修好:空会把语法搅乱,(humidity ____ 40) 被认成类型声明、humidity 染成类型色,
现在空里先填上答案再解析,答案不输出。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-13 19:49:20 +08:00
1 parent 27210eac1b
commit df3b4f0dfd
18 files changed
+345 -462

No files matched your search

@@ -2,7 +2,6 @@
import ExerciseResult from "./ExerciseResult.vue"
import type { Exercise, ExerciseDebugData } from "utils/types"
import { highlightLines } from "shared/highlight"
import "shared/highlight.css"
const props = defineProps<{ exercise: Exercise; lang?: string }>()
const emit = defineEmits<{
@@ -1,8 +1,7 @@
<script setup lang="ts">
import ExerciseResult from "./ExerciseResult.vue"
import type { Exercise, ExerciseFillData } from "utils/types"
import { highlight } from "shared/highlight"
import "shared/highlight.css"
import { highlightWithHoles } from "shared/highlight"
const props = defineProps<{ exercise: Exercise; lang?: string }>()
const emit = defineEmits<{
@@ -16,25 +15,26 @@ type BlankSeg = { type: "blank"; answers: string[]; index: number }
type Segment = CodeSeg | BlankSeg
const segments = computed<Segment[]>(() => {
// 空里先填上第一个答案再解析,免得空把语法搅乱;答案本身不输出,见 highlightWithHoles
const blanks: string[][] = []
const markedCode = data.value.code.replace(/\{\{([^}]+)\}\}/g, (_, inner) => {
blanks.push(inner.split("|"))
return `____${blanks.length - 1}____`
})
const highlighted = highlight(markedCode, props.lang)
const parts = highlighted.split(/____(\d+)____/)
const result: Segment[] = []
for (let i = 0; i < parts.length; i++) {
if (i % 2 === 0) {
if (parts[i]) result.push({ type: "code", html: parts[i] })
} else {
const idx = parseInt(parts[i])
result.push({ type: "blank", answers: blanks[idx], index: idx })
}
const holes: { from: number; to: number }[] = []
let code = ""
let last = 0
for (const m of data.value.code.matchAll(/\{\{([^}]+)\}\}/g)) {
const answers = m[1].split("|")
code += data.value.code.slice(last, m.index)
holes.push({ from: code.length, to: code.length + answers[0].length })
code += answers[0]
blanks.push(answers)
last = m.index + m[0].length
}
return result
code += data.value.code.slice(last)
return highlightWithHoles(code, holes, props.lang).map((part) =>
typeof part === "string"
? { type: "code", html: part }
: { type: "blank", answers: blanks[part], index: part },
)
})
const blankCount = computed(() => segments.value.filter((s) => s.type === "blank").length)
@@ -2,7 +2,6 @@
import ExerciseResult from "./ExerciseResult.vue"
import type { Exercise, ExercisePredictData } from "utils/types"
import { highlight } from "shared/highlight"
import "shared/highlight.css"
const props = defineProps<{ exercise: Exercise; lang?: string }>()
const emit = defineEmits<{
@@ -3,7 +3,6 @@ import ExerciseResult from "./ExerciseResult.vue"
import type { Exercise, ExerciseSortData } from "utils/types"
import { shuffle } from "../composables/useShuffle"
import { highlightLines } from "shared/highlight"
import "shared/highlight.css"
const props = defineProps<{ exercise: Exercise; lang?: string }>()
const emit = defineEmits<{
@@ -11,7 +11,7 @@ import { useDark } from "@vueuse/core"
const props = withDefaults(
defineProps<{
code: string
/** highlight.js 的语言名(LANGUAGE_FORMAT_VALUE) */
/** 高亮用的语言名(LANGUAGE_FORMAT_VALUE),见 shared/highlight.ts */
language: string
/** 要标红的行,1 起 */
markLine?: number | null