- 根目录加入 docker-compose、Caddyfile、文档等原本不在仓库里的文件 - 前后端两份 GitHub Actions 合并成根目录的一个 deploy workflow: 构建前端上传到 build/,再在服务器上 git pull 并重建容器 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.1 KiB
AI Chat Stop Button Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add a stop button to the AI chat panel that cancels in-flight streaming, removes the pending messages from the UI, and ensures nothing is saved to the database or submitted.
Architecture: When the user clicks stop, the frontend pops the unsaved user message from the UI, resets streaming state, and closes the WebSocket. The backend's existing disconnect() handler deletes the in-progress user message from the DB; the assistant message and Submission are never created because complete is never sent.
Tech Stack: Vue 3, TypeScript, Naive UI, WebSocket (Django Channels)
File Map
| File | Change |
|---|---|
client/src/store/prompt.ts |
Store _currentTaskId; set streaming=true on send; add stopPrompt() |
client/src/components/ai/PromptPanel.vue |
Import stopPrompt; swap Send/Stop buttons based on streaming |
Task 1: Update store/prompt.ts
Files:
-
Modify:
client/src/store/prompt.ts -
Step 1: Store
_currentTaskIdand set it inconnectPromptIn
client/src/store/prompt.ts, after the linelet ws: WebSocket | null = null, add:let _currentTaskId = 0Then inside
connectPrompt(taskId: number), add as the very first line of the function body:_currentTaskId = taskIdResult — the start of
connectPromptbecomes:export function connectPrompt(taskId: number) { _currentTaskId = taskId if (ws) ws.close() // ... rest unchanged -
Step 2: Set
streaming = trueimmediately on sendIn
sendPrompt(), addstreaming.value = trueas the first statement before thepush:export function sendPrompt(content: string, model: string = "") { if (!ws || ws.readyState !== WebSocket.OPEN) return streaming.value = true messages.value.push({ role: "user", content }) ws.send(JSON.stringify({ type: "message", content, model })) } -
Step 3: Add
stopPrompt()functionAfter
sendPrompt, add:export function stopPrompt() { // Remove the user message added to UI (not yet saved in DB) if ( messages.value.length > 0 && messages.value[messages.value.length - 1].role === "user" ) { messages.value.pop() } streaming.value = false streamingContent.value = "" // Closing triggers backend disconnect() which deletes the DB user message if (ws) { ws.close() ws = null } // Reconnect immediately so the next message can be sent if (_currentTaskId) { connectPrompt(_currentTaskId) } } -
Step 4: Verify the file looks correct
Open
client/src/store/prompt.tsand confirm:_currentTaskIdis declared afterlet wsconnectPromptsets_currentTaskId = taskIdas its first linesendPromptsetsstreaming.value = truebefore the pushstopPromptis exported and appears betweensendPromptand the privateapplyCode
-
Step 5: Commit
cd /home/xuyue/Projects/webpreview git add client/src/store/prompt.ts git commit -m "feat: add stopPrompt() and track currentTaskId in prompt store"
Task 2: Add Stop button to PromptPanel.vue
Files:
-
Modify:
client/src/components/ai/PromptPanel.vue -
Step 1: Import
stopPromptFind the import line in
<script setup>:import { messages, streaming, streamingContent, sendPrompt, } from "../../store/prompt"Add
stopPromptto the import:import { messages, streaming, streamingContent, sendPrompt, stopPrompt, } from "../../store/prompt" -
Step 2: Replace the send button with conditional Stop/Send buttons
Find the current button block in
<template>:<n-button type="primary" :loading="streaming" :disabled="!input.trim() || streaming" @click="send" > 发送 </n-button>Replace with:
<n-button v-if="streaming" type="error" @click="stopPrompt" > 停止 </n-button> <n-button v-else type="primary" :disabled="!input.trim()" @click="send" > 发送 </n-button> -
Step 3: Verify in browser
Start the dev server:
cd /home/xuyue/Projects/webpreview/client npm run startOpen a challenge page, send a message, confirm:
- The 停止 button appears immediately when send is clicked (no gap)
- Clicking 停止 removes the user message and streaming content from the UI
- The 发送 button reappears and is usable for the next message
- A subsequent message works normally (WS reconnected)
-
Step 4: Commit
cd /home/xuyue/Projects/webpreview git add client/src/components/ai/PromptPanel.vue git commit -m "feat: show stop button during AI streaming in PromptPanel"