Add revert button to user messages
- Add revert icon (↶) button in message header for all user messages - Clicking revert button reverts session to that specific message - Restores the message text back to the prompt input - Style revert button with hover effects and border highlight - Revert action updates UI automatically via SSE session.updated event
This commit is contained in:
38
src/App.tsx
38
src/App.tsx
@@ -77,6 +77,43 @@ const SessionView: Component<{
|
|||||||
await updateSessionModel(props.instanceId, props.sessionId, model)
|
await updateSessionModel(props.instanceId, props.sessionId, model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRevert(messageId: string) {
|
||||||
|
const instance = instances().get(props.instanceId)
|
||||||
|
if (!instance || !instance.client) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log("Reverting to message:", messageId)
|
||||||
|
|
||||||
|
await instance.client.session.revert({
|
||||||
|
path: { id: props.sessionId },
|
||||||
|
body: { messageID: messageId },
|
||||||
|
})
|
||||||
|
|
||||||
|
// Restore the message to input
|
||||||
|
const currentSession = session()
|
||||||
|
if (currentSession) {
|
||||||
|
const revertedMessage = currentSession.messages.find((m) => m.id === messageId)
|
||||||
|
const revertedInfo = currentSession.messagesInfo.get(messageId)
|
||||||
|
|
||||||
|
if (revertedMessage && revertedInfo?.role === "user") {
|
||||||
|
const textParts = revertedMessage.parts.filter((p: any) => p.type === "text")
|
||||||
|
if (textParts.length > 0) {
|
||||||
|
const textarea = document.querySelector(".prompt-input") as HTMLTextAreaElement
|
||||||
|
if (textarea) {
|
||||||
|
textarea.value = textParts.map((p: any) => p.text).join("\n")
|
||||||
|
textarea.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Reverted to message - UI will update via SSE")
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to revert:", error)
|
||||||
|
alert("Failed to revert to message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
when={session()}
|
when={session()}
|
||||||
@@ -94,6 +131,7 @@ const SessionView: Component<{
|
|||||||
messages={s().messages || []}
|
messages={s().messages || []}
|
||||||
messagesInfo={s().messagesInfo}
|
messagesInfo={s().messagesInfo}
|
||||||
revert={s().revert}
|
revert={s().revert}
|
||||||
|
onRevert={handleRevert}
|
||||||
/>
|
/>
|
||||||
<PromptInput
|
<PromptInput
|
||||||
instanceId={props.instanceId}
|
instanceId={props.instanceId}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ interface MessageItemProps {
|
|||||||
message: Message
|
message: Message
|
||||||
messageInfo?: any
|
messageInfo?: any
|
||||||
isQueued?: boolean
|
isQueued?: boolean
|
||||||
|
onRevert?: (messageId: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MessageItem(props: MessageItemProps) {
|
export default function MessageItem(props: MessageItemProps) {
|
||||||
@@ -42,11 +43,27 @@ export default function MessageItem(props: MessageItemProps) {
|
|||||||
return !hasContent() && props.messageInfo?.time?.completed === 0
|
return !hasContent() && props.messageInfo?.time?.completed === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleRevert = () => {
|
||||||
|
if (props.onRevert && isUser()) {
|
||||||
|
props.onRevert(props.message.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={`message-item ${isUser() ? "user" : "assistant"}`}>
|
<div class={`message-item ${isUser() ? "user" : "assistant"}`}>
|
||||||
<div class="message-header">
|
<div class="message-header">
|
||||||
<span class="message-sender">{isUser() ? "You" : "Assistant"}</span>
|
<span class="message-sender">{isUser() ? "You" : "Assistant"}</span>
|
||||||
<span class="message-timestamp">{timestamp()}</span>
|
<span class="message-timestamp">{timestamp()}</span>
|
||||||
|
<Show when={isUser() && props.onRevert}>
|
||||||
|
<button
|
||||||
|
class="message-revert-button"
|
||||||
|
onClick={handleRevert}
|
||||||
|
title="Revert to this message"
|
||||||
|
aria-label="Revert to this message"
|
||||||
|
>
|
||||||
|
↶
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="message-content">
|
<div class="message-content">
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface MessageStreamProps {
|
|||||||
diff?: string
|
diff?: string
|
||||||
}
|
}
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
|
onRevert?: (messageId: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DisplayItem {
|
interface DisplayItem {
|
||||||
@@ -179,7 +180,12 @@ export default function MessageStream(props: MessageStreamProps) {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<MessageItem message={item.data} messageInfo={item.messageInfo} isQueued={item.data.isQueued} />
|
<MessageItem
|
||||||
|
message={item.data}
|
||||||
|
messageInfo={item.messageInfo}
|
||||||
|
isQueued={item.data.isQueued}
|
||||||
|
onRevert={props.onRevert}
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -188,6 +188,33 @@ body {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-revert-button {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1;
|
||||||
|
transition: all 0.2s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 28px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-revert-button:hover {
|
||||||
|
background-color: var(--hover-bg);
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-revert-button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
.message-content {
|
.message-content {
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
|
|||||||
Reference in New Issue
Block a user