- 根目录加入 docker-compose、Caddyfile、文档等原本不在仓库里的文件 - 前后端两份 GitHub Actions 合并成根目录的一个 deploy workflow: 构建前端上传到 build/,再在服务器上 git pull 并重建容器 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.9 KiB
Challenge AI Chat Improvements Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: 折叠 AI 对话中的代码块,并在 AI 生成代码后自动静默提交作品。
Architecture: 在 PromptPanel.vue 中用自定义 marked renderer 将代码块渲染为 <details>/<summary>;在 prompt.ts 中暴露 onCodeComplete 回调,ChallengeHome.vue 注册回调触发静默提交。
Tech Stack: Vue 3, TypeScript, marked (custom renderer), Naive UI, WebSocket (already in use)
Task 1: 代码块折叠 — 自定义 marked renderer
Files:
- Modify:
client/src/components/PromptPanel.vue
Step 1: 在 <script setup> 中创建局部 marked 实例并覆盖 code renderer
在 PromptPanel.vue 的 <script setup> 中,替换现有的 renderMarkdown 函数:
import { marked, Renderer } from "marked"
const renderer = new Renderer()
const _origCode = renderer.code.bind(renderer)
renderer.code = function ({ text, lang }) {
const label = lang ? `查看代码(${lang})` : "查看代码"
// 对代码内容做 HTML 转义,防止 XSS
const escaped = text
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
return `<details class="code-block"><summary>${label}</summary><pre><code class="hljs${lang ? ` language-${lang}` : ""}">${escaped}</code></pre></details>`
}
function renderMarkdown(text: string): string {
return marked.parse(text, { renderer }) as string
}
注意:marked 已在 main.ts 全局配置了 markedHighlight,但局部 renderer 不继承全局高亮插件。代码块在聊天中默认折叠,用户主要通过预览区查看效果,折叠后不高亮可接受。若需要高亮,可在 details 展开后用 hljs.highlightAll() 触发,属于可选优化。
Step 2: 在 <style scoped> 中添加折叠样式
.message-content :deep(details.code-block) {
border: 1px solid #e0e0e0;
border-radius: 4px;
margin: 6px 0;
}
.message-content :deep(details.code-block summary) {
padding: 4px 10px;
cursor: pointer;
font-size: 12px;
color: #666;
user-select: none;
background: #f5f5f5;
border-radius: 4px;
}
.message-content :deep(details.code-block[open] summary) {
border-bottom: 1px solid #e0e0e0;
border-radius: 4px 4px 0 0;
}
.message-content :deep(details.code-block pre) {
margin: 0;
border-radius: 0 0 4px 4px;
}
Step 3: 验证
启动开发服务器 cd client && npm run start,打开一个挑战页面,发送一条会生成代码的提示词(如"做一个蓝色的圆形按钮"),确认:
- AI 回复中的代码块显示为折叠的
<details>元素 - 点击 summary 可展开查看代码
- 流式输出时也正确折叠
Step 4: Commit
git add client/src/components/PromptPanel.vue
git commit -m "feat: collapse code blocks in AI chat messages"
Task 2: 自动提交 — prompt.ts 暴露回调
Files:
- Modify:
client/src/store/prompt.ts
Step 1: 在文件顶部添加回调导出
在 export const streamingContent 那行后面添加:
export let onCodeComplete: ((code: { html: string | null; css: string | null; js: string | null }) => void) | null = null
Step 2: 在 complete 事件处理中调用回调
找到 } else if (data.type === "complete") { 块,在 applyCode(data.code) 调用后追加:
if (data.code && onCodeComplete) {
onCodeComplete(data.code)
}
完整的 complete 块变为:
} else if (data.type === "complete") {
streaming.value = false
messages.value.push({
role: "assistant",
content: streamingContent.value,
code: data.code,
})
streamingContent.value = ""
if (data.code) {
applyCode(data.code)
if (onCodeComplete) {
onCodeComplete(data.code)
}
}
}
Step 3: 在 disconnectPrompt 中清除回调
在 disconnectPrompt() 函数中添加:
onCodeComplete = null
Step 4: Commit
git add client/src/store/prompt.ts
git commit -m "feat: expose onCodeComplete callback in prompt store"
Task 3: 自动提交 — ChallengeHome.vue 注册回调
Files:
- Modify:
client/src/pages/ChallengeHome.vue
Step 1: 导入 onCodeComplete
在现有 import 行中,将 prompt store 的导入补充 onCodeComplete:
import { connectPrompt, disconnectPrompt, conversationId, streaming, onCodeComplete } from "../store/prompt"
注意:onCodeComplete 是 let 导出的普通变量,不是 ref,直接赋值即可。
Step 2: 在 loadChallenge 中注册静默提交回调
在 connectPrompt(data.task_ptr) 调用后添加:
onCodeComplete = async () => {
if (!conversationId.value) return
try {
await Submission.create(taskId.value, {
html: html.value,
css: css.value,
js: js.value,
conversationId: conversationId.value,
})
} catch {
// 静默失败,不打扰用户
}
}
Step 3: 验证
在挑战页面发送生成代码的请求,打开浏览器 Network 面板,确认:
- AI 回复完成(含代码)后,自动发出
POST /api/submission/请求 - 无任何 loading 状态或弹窗
- 手动点击"提交作品"按钮仍然正常工作
- 返回挑战列表再进入,
onCodeComplete不会指向旧页面的 submit 函数(disconnectPrompt会清除)
Step 4: Commit
git add client/src/pages/ChallengeHome.vue
git commit -m "feat: auto-submit on AI code generation in challenge"
完成标准
- AI 对话中所有代码块默认折叠,可点击展开
- 流式输出期间代码块也处于折叠状态
- 每次 AI 生成含代码的回复后,自动静默提交一次
- 自动提交失败时无任何 UI 反馈(静默)
- 手动"提交作品"按钮功能不受影响
- 离开挑战页面后回调被正确清理,不产生内存泄漏