feat(chat): add message editing & regeneration (M8.1)

Pencil icon on hover for user messages to edit inline. Regenerate
icon on assistant messages to re-send from the same prompt. Editing
clears all subsequent messages and triggers a fresh AI response.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-03 23:01:48 +00:00
co-authored by Claude Opus 4.6
parent 9f6158ddd2
commit 61a6e88667
5 changed files with 251 additions and 21 deletions
+109
View File
@@ -355,9 +355,118 @@ export function useAI() {
}
}
/**
* Edit a user message and regenerate the AI response.
* Clears all messages after the edited message, then re-sends.
*/
async function editAndResend(messageId: string, newContent: string) {
const convId = chatStore.activeConversationId
if (!convId) return
const conv = chatStore.activeConversation
if (!conv) return
const msgIndex = conv.messages.findIndex((m) => m.id === messageId)
if (msgIndex === -1) return
// Update the message content
chatStore.updateMessageContent(convId, messageId, newContent)
// Delete all messages after this one
chatStore.deleteMessagesAfter(convId, msgIndex + 1)
// Re-send (creates new assistant message and streams)
await resendLastUserMessage()
}
/**
* Regenerate the last assistant response.
* Deletes the last assistant message and re-sends with the same user message.
*/
async function regenerateLastResponse() {
const convId = chatStore.activeConversationId
if (!convId) return
const conv = chatStore.activeConversation
if (!conv || conv.messages.length === 0) return
// Find the last assistant message and remove it
const lastIndex = conv.messages.length - 1
if (conv.messages[lastIndex].role === 'assistant') {
chatStore.deleteMessagesAfter(convId, lastIndex)
}
await resendLastUserMessage()
}
/** Internal: re-send from the current last user message */
async function resendLastUserMessage() {
const convId = chatStore.activeConversationId
if (!convId) return
const conv = chatStore.activeConversation
if (!conv || conv.messages.length === 0) return
const lastUserMsg = [...conv.messages].reverse().find((m) => m.role === 'user')
if (!lastUserMsg) return
const provider = activeProvider.value
currentAbort = new AbortController()
const signal = currentAbort.signal
const cid = convId
const assistantMsg = chatStore.addMessage(cid, { role: 'assistant', content: '' })
if (!assistantMsg) return
chatStore.isStreaming = true
let systemPrompt = SYSTEM_PROMPT
if (chatStore.webSearchEnabled) {
systemPrompt += `
**Web search:** You have access to WebSearch and WebFetch tools. Use them to look up current information, news, and facts when the user asks. You can search the web and fetch page content. Web search is enabled for this session—do not tell the user it is unavailable.`
}
if (chatStore.webSearchEnabled && lastUserMsg.content.trim()) {
const results = await searchWeb(lastUserMsg.content)
if (results.length > 0) {
systemPrompt += formatWebSearchContext(results)
chatStore.setMessageWebResults(cid, assistantMsg.id, results)
}
}
const history: ChatMessage[] = chatStore.messages
.filter((m) => m.id !== assistantMsg.id)
.map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content }))
const onToken = (text: string) => chatStore.appendToLastMessage(cid, text)
const onError = (err: string) => {
console.error(`[AIUI ${provider}]`, err)
chatStore.appendToLastMessage(cid, `${err}`)
}
try {
if (provider === 'claude') {
await streamClaude(history, onToken, onError, systemPrompt, chatStore.webSearchEnabled, signal)
} else if (provider === 'openrouter') {
await streamOpenRouter(history, onToken, onError, systemPrompt, signal)
} else {
await streamMock(history, onToken, signal)
}
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') return
const msg = err instanceof Error ? err.message : String(err)
chatStore.appendToLastMessage(cid, `\n\n⚠ Connection error: ${msg}`)
} finally {
currentAbort = null
chatStore.isStreaming = false
}
}
return {
sendMessage,
stopGeneration,
editAndResend,
regenerateLastResponse,
activeProvider,
activeModel,
availableProviders,