diff --git a/CLAUDE.md b/CLAUDE.md index c5ece64..ba7d56e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ The backend was rewritten from Django in 2026-10 to cut memory use (~25 MB vs. 1 - `src/auth.ts` — Django-compatible password hashing; JWT login, no cookies: `POST /account/login` returns a 15-min HS256 `access_token` (sent as `Authorization: Bearer`, signed with `JWT_SECRET`, random per process if unset) and a 14-day opaque `refresh_token` (only its sha256 is stored, in `auth_refresh_token`; `POST /account/refresh` trades it for a new access token, logout / password change / deactivation delete it). An invalid or expired Bearer token is a 401 even on public endpoints so the client knows to refresh. `requireUser/requireAdmin/requireSuper` (401 / 403) - `src/routes/` — `account`, `tasks` (tutorial / challenge / creation), `files` (task assets + image upload), `submission`, `showcase` (mounted under `/submission/showcase`), `prompt` - `src/services/` — `score.ts` (rating weights super×0.5, admin×0.3, normal×0.2, then Bayesian smoothing with C=3 and zones), `gradebook.ts`, `llm.ts` (OpenAI-compatible streaming via fetch; DeepSeek default, ARK for doubao models; chat context is the last 20 messages with older code blocks elided), `classifier.ts` (Bloom-level L1–L6 prompt tagging, runs in background), `cascade.ts` (Django's CASCADE deletes done by hand, plus the shared delete rules: `hasRatingsOrAwards`, `deleteRound`), `pyround.ts` (`pyRound`/`pySum` reproduce Python's `round()` and `sum()` so scores match the old system bit-for-bit) - - `src/ws.ts` — `/ws/prompt//?token=` (code generation chat) and `/ws/guidance//?token=...` (prompt coaching); one message per connection at a time (a message sent while the AI is still replying gets an error, payloads capped at 64 KB); on disconnect the pending user message is deleted and the LLM request aborted + - `src/ws.ts` — `/ws/prompt//?token=` (code generation chat; the AI context and the page's chat include `conversation`, `edit` and `manual` messages) and `/ws/guidance//?token=...` (prompt coaching); one message per connection at a time (a message sent while the AI is still replying gets an error, payloads capped at 64 KB); on disconnect the pending user message is deleted and the LLM request aborted - `scripts/` — one-off CLIs: `create-superuser.ts`, `classify-prompts.ts`, `recalculate-scores.ts` - Roles: `super`, `admin`, `normal`. Task model is multi-table: `task_task` + one of `task_tutorial` / `task_challenge` / `task_creation` (`task_ptr_id`) @@ -29,6 +29,7 @@ The backend was rewritten from Django in 2026-10 to cut memory use (~25 MB vs. 1 - `src/router.ts` — Vue Router; `meta.auth` needs login, `meta.role` (`staff` = admin+super, `super`) is checked in the guard after `store/session.loadUser()`, `meta.bare` pages render without the top bar - `src/theme.ts` — the only place colors are defined: one palette (light + dark values) becomes both `--wp-*` CSS variables and Naive UI `themeOverrides`; use `var(--wp-...)` in styles, never hard-coded colors. No `color-mix()` (Chrome 105) - `src/icons.ts` — `` renders bundled `lucide-vue-next` icons (works offline); register new names in its map. Fonts are bundled too (`@fontsource/jetbrains-mono`) + - `components/workbench/Workbench.vue` — shared challenge / creation page: left = brief + AI chat (`PromptPanel`), right = preview / code (`CodeTabs`) + `VersionBar`. Every assistant message with code is a version; hand edits and pasted external-AI code are saved as versions via `POST /prompt/versions` (`source` `edit` / `manual`, stored as a user+assistant pair). Challenges are submitted only when the student clicks “交这一版” (`POST /submission/` with `message_id`), never automatically. Editor code is per task (`composables/useTaskCode.ts`, localStorage `web-code::`); the tutorial page still uses the shared `store/editors.ts` - `components/layout/AppShell.vue` — top bar for every page; `pages/Dashboard.vue` is the teaching sidebar (“教学”) under `/dashboard` - `src/store/` — Reactive state (plain Vue refs, not Vuex/Pinia); `prompt.ts` / `guidance.ts` own the WebSockets - `src/pages/`, `src/components/` — user management uses `components/dashboard/UserFormModal.vue` (there is no Django Admin anymore) diff --git a/client/src/api.ts b/client/src/api.ts index a196ad1..780dafc 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -395,6 +395,20 @@ export const Gradebook = { } export const Prompt = { + // 保存一个不是 AI 生成的版本:手改的代码,或者带提示词的外部 AI 代码 + async saveVersion( + taskId: number, + code: { html: string; css: string; js: string }, + prompt?: string, + ): Promise<{ message_id: number; user_message_id: number; source: string }> { + const res = await http.post("/prompt/versions", { + task_id: taskId, + ...code, + prompt: prompt || undefined, + }) + return res.data + }, + async listConversations(taskId?: number, userId?: number) { const params: Record = {} if (taskId) params.task_id = taskId diff --git a/client/src/components/ai/ExternalAIPanel.vue b/client/src/components/ai/ExternalAIPanel.vue deleted file mode 100644 index 71ea019..0000000 --- a/client/src/components/ai/ExternalAIPanel.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - diff --git a/client/src/components/ai/PromptHistoryPanel.vue b/client/src/components/ai/PromptHistoryPanel.vue index bd6bf97..9f6ec45 100644 --- a/client/src/components/ai/PromptHistoryPanel.vue +++ b/client/src/components/ai/PromptHistoryPanel.vue @@ -70,9 +70,9 @@ - {{ item.source === "manual" ? "手动提交" : "AI 对话" }} + {{ sourceLabel(item.source) }} @@ -133,6 +133,7 @@ import { onMounted, ref, watch } from "vue" import { Icon } from "../../icons" import { useMessage } from "naive-ui" import { renderMarkdown } from "../../utils/markdown" +import { sourceLabel } from "../../utils/helper" import { Prompt } from "../../api" import type { PromptHistoryItem } from "../../utils/type" import { parseTime } from "../../utils/helper" diff --git a/client/src/components/ai/PromptPanel.vue b/client/src/components/ai/PromptPanel.vue index 45369fc..33214d7 100644 --- a/client/src/components/ai/PromptPanel.vue +++ b/client/src/components/ai/PromptPanel.vue @@ -18,8 +18,15 @@ -
-
我
+
+ 你手动修改了代码 +
+
+
+ 我 + 用外部 AI +
-
AI
-
-
- - {{ pair.assistantMsg.submitted ? "已提交" : "提交此版本" }} - + +
+ v{{ versionOf(pair.assistantMsg) }} + 已提交
@@ -62,6 +62,7 @@ >
AI 正在生成中…
+
@@ -80,18 +81,22 @@ style="flex: 1; min-width: 0" @keydown.enter.exact.prevent="send" /> - -