feat(chat): Add undo button to trim conversation to a prior message
Lets you click Undo under a user message to drop it and everything after it, refilling the prompt with that message's text/images so it can be edited and resent.
This commit is contained in:
+35
-8
@@ -97,6 +97,23 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
.undo-btn {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
color: var(--text-user);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
font-size: 0.8em;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.undo-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
.system-message {
|
.system-message {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
@@ -283,6 +300,7 @@
|
|||||||
<img :src="img.dataUrl" style="max-width: 100%; border-radius: 0.3rem; display: block; margin-bottom: 0.3rem;" />
|
<img :src="img.dataUrl" style="max-width: 100%; border-radius: 0.3rem; display: block; margin-bottom: 0.3rem;" />
|
||||||
</template>
|
</template>
|
||||||
<div class="text" x-text="msg.content"></div>
|
<div class="text" x-text="msg.content"></div>
|
||||||
|
<button class="undo-btn" title="Trim conversation back to before this message" @click="trimTo(msg)">Undo</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="msg.role === 'assistant'">
|
<template x-if="msg.role === 'assistant'">
|
||||||
@@ -355,9 +373,8 @@
|
|||||||
You are a helpful AI assistant.
|
You are a helpful AI assistant.
|
||||||
- Do not preach about implications, do not praise excessively.
|
- Do not preach about implications, do not praise excessively.
|
||||||
- Keep a critical mindset, do not assume blindly.
|
- Keep a critical mindset, do not assume blindly.
|
||||||
- Answer in valid markdown only, no exceptions.
|
|
||||||
|
|
||||||
- When I ask something about code, or software development, keep in mind that I am a senior backend developer (but do not mention that explicitly), and do not explain excessively, but and challenge my statements with better alternatives if I don't sound right, especially in coding. Do not bring it up otherwise.
|
- When I ask something about code, or software development, keep in mind that I am a senior backend developer (but do not mention that explicitly), and do not explain excessively, but and challenge my statements where needed with better alternatives if I don't sound right, especially in coding. Do not bring it up otherwise.
|
||||||
- Do not give me code if the topic is not about programming or software development.
|
- Do not give me code if the topic is not about programming or software development.
|
||||||
`
|
`
|
||||||
.replace(/^\s+/gm, "")
|
.replace(/^\s+/gm, "")
|
||||||
@@ -375,9 +392,9 @@
|
|||||||
sessionId: null,
|
sessionId: null,
|
||||||
|
|
||||||
models: [
|
models: [
|
||||||
{ name: "gpt-5.4-mini", chat: chatWithOpenAI, key: "OPENAI_API_KEY" },
|
{ name: "gpt-5.4-mini", chat: chatWithOpenAI, key: window.env.OPENAI_API_KEY },
|
||||||
{ name: "gemini-3.5-flash-lite", chat: chatWithGemini, key: "GEMINI_API_KEY" },
|
{ name: "gemini-3.5-flash-lite", chat: chatWithGemini, key: window.env.GEMINI_API_KEY },
|
||||||
{ name: "claude-haiku-4-5-20251001", chat: chatWithClaude, key: "ANTHROPIC_API_KEY" },
|
{ name: "claude-haiku-4-5-20251001", chat: chatWithClaude, key: window.env.ANTHROPIC_API_KEY },
|
||||||
],
|
],
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@@ -444,7 +461,7 @@
|
|||||||
const modelObj = this.models.find((m) => m.name === this.activeModelName);
|
const modelObj = this.models.find((m) => m.name === this.activeModelName);
|
||||||
await modelObj.chat({
|
await modelObj.chat({
|
||||||
model: this.activeModelName,
|
model: this.activeModelName,
|
||||||
apiKey: window.env[modelObj.key],
|
apiKey: modelObj.key,
|
||||||
messages: this.messages,
|
messages: this.messages,
|
||||||
signal: this.abortController.signal,
|
signal: this.abortController.signal,
|
||||||
onContent: (chunk) => {
|
onContent: (chunk) => {
|
||||||
@@ -492,6 +509,17 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
trimTo(msg) {
|
||||||
|
if (this.isBusy) return;
|
||||||
|
const idx = this.messages.indexOf(msg);
|
||||||
|
if (idx === -1) return;
|
||||||
|
this.messages = this.messages.slice(0, idx);
|
||||||
|
updateSessionHistory(this.sessionId, this.messages);
|
||||||
|
this.promptText = msg.content;
|
||||||
|
this.pendingImages = msg.images ? JSON.parse(JSON.stringify(msg.images)) : [];
|
||||||
|
this.$nextTick(() => this.$refs.promptInput.focus());
|
||||||
|
},
|
||||||
|
|
||||||
restoreConversation(msgs) {
|
restoreConversation(msgs) {
|
||||||
this.messages = JSON.parse(JSON.stringify(msgs));
|
this.messages = JSON.parse(JSON.stringify(msgs));
|
||||||
this.userHasScrolledUp = false;
|
this.userHasScrolledUp = false;
|
||||||
@@ -564,11 +592,10 @@
|
|||||||
"anthropic-version": "2023-06-01",
|
"anthropic-version": "2023-06-01",
|
||||||
"anthropic-dangerous-direct-browser-access": "true",
|
"anthropic-dangerous-direct-browser-access": "true",
|
||||||
},
|
},
|
||||||
apiKey: window.env.ANTHROPIC_API_KEY,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function chatWithGemini({ onContent, onError, messages, signal, apiKey = window.env.GEMINI_API_KEY, model, ...rest }) {
|
async function chatWithGemini({ onContent, onError, messages, signal, apiKey, model, ...rest }) {
|
||||||
const contents = messages.map((msg, i, arr) => {
|
const contents = messages.map((msg, i, arr) => {
|
||||||
const role = msg.role === "assistant" ? "model" : "user";
|
const role = msg.role === "assistant" ? "model" : "user";
|
||||||
let parts = [{ text: msg.content }];
|
let parts = [{ text: msg.content }];
|
||||||
|
|||||||
Reference in New Issue
Block a user