feat: AI生成回复时显示停止生成按钮

This commit is contained in:
2026-04-26 17:54:01 +08:00
parent d550461e0b
commit 691b7f0e08
3 changed files with 88 additions and 3 deletions

View File

@@ -781,6 +781,9 @@ async function streamGenerate(userMsgIndex) {
} }
contentEl.innerHTML = '<span class="streaming-cursor">▌</span>'; contentEl.innerHTML = '<span class="streaming-cursor">▌</span>';
// 显示停止生成按钮
showStopGenerateBtn();
try { try {
// 构建请求体 - 统一使用 glm-4.5-air通过 thinking 参数控制 // 构建请求体 - 统一使用 glm-4.5-air通过 thinking 参数控制
@@ -814,8 +817,30 @@ async function streamGenerate(userMsgIndex) {
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = ''; let buffer = '';
let thinkingOutputStarted = false; // 正式内容是否开始输出 let thinkingOutputStarted = false; // 正式内容是否开始输出
let abortController = new AbortController(); // 用于中断流
// 绑定停止按钮事件
const stopBtn = document.getElementById('stopGenerateBtn');
if (stopBtn) {
stopBtn.onclick = () => {
abortController.abort();
isLoading = false;
sendBtn.disabled = false;
hideStopGenerateBtn();
// 更新最终内容
if (thinkingEl && enableThinking && currentConversation.messages[aiMessageIndex].thinking) {
thinkingEl.innerHTML = renderMarkdown(currentConversation.messages[aiMessageIndex].thinking);
}
contentEl.innerHTML = renderMarkdown(currentConversation.messages[aiMessageIndex].content);
currentConversation.updatedAt = Date.now();
saveConversations();
renderMessages();
};
}
while (true) { while (true) {
if (abortController.signal.aborted) break; // 检查是否已停止
const { done, value } = await reader.read(); const { done, value } = await reader.read();
if (done) break; if (done) break;
@@ -877,6 +902,7 @@ async function streamGenerate(userMsgIndex) {
} finally { } finally {
isLoading = false; isLoading = false;
sendBtn.disabled = false; sendBtn.disabled = false;
hideStopGenerateBtn();
currentConversation.updatedAt = Date.now(); currentConversation.updatedAt = Date.now();
saveConversations(); saveConversations();
renderMessages(); renderMessages();
@@ -889,6 +915,33 @@ async function streamGenerate(userMsgIndex) {
} }
} }
// 显示停止生成按钮
function showStopGenerateBtn() {
// 检查是否已存在
if (document.getElementById('stopGenerateBtn')) return;
const stopBtn = document.createElement('button');
stopBtn.id = 'stopGenerateBtn';
stopBtn.className = 'stop-generate-btn';
stopBtn.innerHTML = `
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M6 6h12v12H6z"/></svg>
<span>停止生成</span>
`;
// 插入到消息容器底部
if (messagesContainer) {
messagesContainer.appendChild(stopBtn);
}
}
// 隐藏停止生成按钮
function hideStopGenerateBtn() {
const stopBtn = document.getElementById('stopGenerateBtn');
if (stopBtn) {
stopBtn.remove();
}
}
// 生成对话标题 // 生成对话标题
async function generateConversationTitle() { async function generateConversationTitle() {
if (!currentConversation) return; if (!currentConversation) return;

View File

@@ -8,12 +8,12 @@
<meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0"> <meta http-equiv="Expires" content="0">
<title>AI助手</title> <title>AI助手</title>
<link rel="stylesheet" href="style.css?v=2.5.2"> <link rel="stylesheet" href="style.css?v=2.6.0">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script src="marked.min.js?v=2.5.2"></script> <script src="marked.min.js?v=2.6.0"></script>
<script src="app.js?v=2.5.2"></script> <script src="app.js?v=2.6.0"></script>
</body> </body>
</html> </html>

View File

@@ -1055,4 +1055,36 @@ body {
.input-area { .input-area {
padding-bottom: calc(12px + env(safe-area-inset-bottom)); padding-bottom: calc(12px + env(safe-area-inset-bottom));
} }
}
/* 停止生成按钮 */
.stop-generate-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 24px;
margin: 16px auto;
background: linear-gradient(135deg, #e53e3e 0%, #c53030 100%);
color: white;
border: none;
border-radius: 24px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(229, 62, 62, 0.3);
}
.stop-generate-btn:hover {
transform: scale(1.05);
box-shadow: 0 4px 16px rgba(229, 62, 62, 0.4);
}
.stop-generate-btn:active {
transform: scale(0.98);
}
.stop-generate-btn svg {
flex-shrink: 0;
} }