From 2991de528ab2f6968b698257084924fe75837f0f Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 26 Feb 2026 13:46:48 +0000 Subject: [PATCH] feat(ui): add delete-up-to action and range hover overlay --- .../ui/src/components/message-block-list.tsx | 2 + packages/ui/src/components/message-block.tsx | 187 ++++++++++++++++-- packages/ui/src/components/message-item.tsx | 51 ++++- .../ui/src/components/message-preview.tsx | 2 + .../ui/src/components/message-section.tsx | 3 + .../ui/src/components/message-timeline.tsx | 12 ++ .../src/components/session/session-view.tsx | 39 +++- .../ui/src/lib/i18n/messages/en/messaging.ts | 5 +- .../ui/src/lib/i18n/messages/en/session.ts | 2 + .../ui/src/lib/i18n/messages/es/messaging.ts | 5 +- .../ui/src/lib/i18n/messages/es/session.ts | 2 + .../ui/src/lib/i18n/messages/fr/messaging.ts | 5 +- .../ui/src/lib/i18n/messages/fr/session.ts | 2 + .../ui/src/lib/i18n/messages/ja/messaging.ts | 5 +- .../ui/src/lib/i18n/messages/ja/session.ts | 2 + .../ui/src/lib/i18n/messages/ru/messaging.ts | 5 +- .../ui/src/lib/i18n/messages/ru/session.ts | 2 + .../lib/i18n/messages/zh-Hans/messaging.ts | 5 +- .../src/lib/i18n/messages/zh-Hans/session.ts | 2 + .../src/styles/messaging/delete-overlays.css | 2 - .../src/styles/messaging/message-timeline.css | 1 - packages/ui/src/types/delete-hover.ts | 1 + 22 files changed, 303 insertions(+), 39 deletions(-) diff --git a/packages/ui/src/components/message-block-list.tsx b/packages/ui/src/components/message-block-list.tsx index 8b2288b4..2d5a382b 100644 --- a/packages/ui/src/components/message-block-list.tsx +++ b/packages/ui/src/components/message-block-list.tsx @@ -22,6 +22,7 @@ interface MessageBlockListProps { scrollContainer: Accessor loading?: boolean onRevert?: (messageId: string) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise onFork?: (messageId?: string) => void onContentRendered?: () => void deleteHover?: Accessor @@ -57,6 +58,7 @@ export default function MessageBlockList(props: MessageBlockListProps) { deleteHover={props.deleteHover} onDeleteHoverChange={props.onDeleteHoverChange} onRevert={props.onRevert} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} onFork={props.onFork} onContentRendered={props.onContentRendered} /> diff --git a/packages/ui/src/components/message-block.tsx b/packages/ui/src/components/message-block.tsx index 7b18047d..bc125afe 100644 --- a/packages/ui/src/components/message-block.tsx +++ b/packages/ui/src/components/message-block.tsx @@ -1,5 +1,5 @@ import { For, Match, Show, Switch, createEffect, createMemo, createSignal, untrack } from "solid-js" -import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, Trash2 } from "lucide-solid" +import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, ListStart, Trash } from "lucide-solid" import MessageItem from "./message-item" import ToolCall from "./tool-call" import type { InstanceMessageStore } from "../stores/message-v2/instance-store" @@ -16,6 +16,14 @@ import { deleteMessage } from "../stores/session-actions" import { useI18n } from "../lib/i18n" import type { DeleteHoverState } from "../types/delete-hover" +function DeleteUpToIcon() { + return ( + + ) +} + const TOOL_ICON = "🔧" const USER_BORDER_COLOR = "var(--message-user-border)" const ASSISTANT_BORDER_COLOR = "var(--message-assistant-border)" @@ -195,6 +203,7 @@ interface MessageContentItemProps { messageIndex: number lastAssistantIndex: () => number onRevert?: (messageId: string) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise onFork?: (messageId?: string) => void onContentRendered?: () => void showDeleteMessage?: boolean @@ -288,6 +297,7 @@ function MessageContentItem(props: MessageContentItemProps) { showDeleteMessage={props.showDeleteMessage} onDeleteHoverChange={props.onDeleteHoverChange} onRevert={props.onRevert} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} onFork={props.onFork} onContentRendered={props.onContentRendered} /> @@ -305,11 +315,13 @@ interface ToolCallItemProps { onContentRendered?: () => void showDeleteMessage?: boolean onDeleteHoverChange?: (state: DeleteHoverState) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise } function ToolCallItem(props: ToolCallItemProps) { const { t } = useI18n() const [deletingMessage, setDeletingMessage] = createSignal(false) + const [deletingUpTo, setDeletingUpTo] = createSignal(false) const record = createMemo(() => props.store().getMessage(props.messageId)) const messageInfo = createMemo(() => props.store().getMessageInfo(props.messageId)) @@ -370,6 +382,21 @@ function ToolCallItem(props: ToolCallItemProps) { } } + const handleDeleteUpTo = async (event: MouseEvent) => { + event.preventDefault() + event.stopPropagation() + if (!props.showDeleteMessage) return + if (!props.onDeleteMessagesUpTo) return + if (deletingUpTo()) return + + setDeletingUpTo(true) + try { + await props.onDeleteMessagesUpTo(props.messageId) + } finally { + setDeletingUpTo(false) + } + } + return ( {(resolvedToolPart) => ( @@ -396,6 +423,19 @@ function ToolCallItem(props: ToolCallItemProps) { + + @@ -477,6 +517,7 @@ interface MessageBlockProps { deleteHover?: () => DeleteHoverState onDeleteHoverChange?: (state: DeleteHoverState) => void onRevert?: (messageId: string) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise onFork?: (messageId?: string) => void onContentRendered?: () => void } @@ -489,7 +530,21 @@ export default function MessageBlock(props: MessageBlockProps) { const isDeleteMessageHovered = () => { const hover = props.deleteHover?.() ?? ({ kind: "none" } as DeleteHoverState) - return hover.kind === "message" && hover.messageId === props.messageId + + if (hover.kind === "message") { + return hover.messageId === props.messageId + } + + if (hover.kind === "deleteUpTo") { + const ids = props.store().getSessionMessageIds(props.sessionId) + const targetIndex = ids.indexOf(hover.messageId) + if (targetIndex === -1) return false + const currentIndex = ids.indexOf(props.messageId) + if (currentIndex === -1) return false + return currentIndex >= targetIndex + } + + return false } const block = createMemo(() => { @@ -699,6 +754,7 @@ export default function MessageBlock(props: MessageBlockProps) { showDeleteMessage={index() === 0} onDeleteHoverChange={props.onDeleteHoverChange} onRevert={props.onRevert} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} onFork={props.onFork} onContentRendered={props.onContentRendered} /> @@ -716,6 +772,7 @@ export default function MessageBlock(props: MessageBlockProps) { partId={toolItem.partId} showDeleteMessage={index() === 0} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} onContentRendered={props.onContentRendered} /> @@ -733,6 +790,7 @@ export default function MessageBlock(props: MessageBlockProps) { sessionId={props.sessionId} messageId={props.messageId} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} /> @@ -747,6 +805,7 @@ export default function MessageBlock(props: MessageBlockProps) { sessionId={props.sessionId} messageId={props.messageId} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} /> @@ -759,6 +818,7 @@ export default function MessageBlock(props: MessageBlockProps) { messageId={(item as CompactionDisplayItem).messageId} showDeleteMessage={index() === 0} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} /> @@ -772,6 +832,7 @@ export default function MessageBlock(props: MessageBlockProps) { defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded} showDeleteMessage={index() === 0} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} /> @@ -795,6 +856,7 @@ interface StepCardProps { sessionId?: string messageId?: string onDeleteHoverChange?: (state: DeleteHoverState) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise } interface CompactionCardProps { @@ -806,11 +868,13 @@ interface CompactionCardProps { messageId: string showDeleteMessage?: boolean onDeleteHoverChange?: (state: DeleteHoverState) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise } function CompactionCard(props: CompactionCardProps) { const { t } = useI18n() const [deletingMessage, setDeletingMessage] = createSignal(false) + const [deletingUpTo, setDeletingUpTo] = createSignal(false) const isAuto = () => Boolean((props.part as any)?.auto) const label = () => (isAuto() ? t("messageBlock.compaction.autoLabel") : t("messageBlock.compaction.manualLabel")) const borderColor = () => props.borderColor ?? (isAuto() ? "var(--session-status-compacting-fg)" : USER_BORDER_COLOR) @@ -839,6 +903,21 @@ function CompactionCard(props: CompactionCardProps) { } } + const handleDeleteUpTo = async (event: MouseEvent) => { + event.preventDefault() + event.stopPropagation() + if (!props.showDeleteMessage) return + if (!props.onDeleteMessagesUpTo) return + if (deletingUpTo()) return + + setDeletingUpTo(true) + try { + await props.onDeleteMessagesUpTo(props.messageId) + } finally { + setDeletingUpTo(false) + } + } + return (
+ +
@@ -874,6 +966,7 @@ function CompactionCard(props: CompactionCardProps) { function StepCard(props: StepCardProps) { const { t } = useI18n() const [deletingMessage, setDeletingMessage] = createSignal(false) + const [deletingUpTo, setDeletingUpTo] = createSignal(false) const timestamp = () => { const value = props.messageInfo?.time?.created ?? (props.part as any)?.time?.start ?? Date.now() const date = new Date(value) @@ -939,6 +1032,21 @@ function StepCard(props: StepCardProps) { } } + const handleDeleteUpTo = async (event: MouseEvent) => { + event.preventDefault() + event.stopPropagation() + if (!props.messageId) return + if (!props.onDeleteMessagesUpTo) return + if (deletingUpTo()) return + + setDeletingUpTo(true) + try { + await props.onDeleteMessagesUpTo(props.messageId) + } finally { + setDeletingUpTo(false) + } + } + const renderUsageChips = (usage: NonNullable>) => { const entries = [ @@ -971,18 +1079,33 @@ function StepCard(props: StepCardProps) { return (
- +
+ + + +
{renderUsageChips(usage)} @@ -1025,12 +1148,14 @@ interface ReasoningCardProps { defaultExpanded?: boolean showDeleteMessage?: boolean onDeleteHoverChange?: (state: DeleteHoverState) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise } function ReasoningCard(props: ReasoningCardProps) { const { t } = useI18n() const [expanded, setExpanded] = createSignal(Boolean(props.defaultExpanded)) const [deletingMessage, setDeletingMessage] = createSignal(false) + const [deletingUpTo, setDeletingUpTo] = createSignal(false) createEffect(() => { setExpanded(Boolean(props.defaultExpanded)) @@ -1118,6 +1243,21 @@ function ReasoningCard(props: ReasoningCardProps) { } } + const handleDeleteUpTo = async (event: MouseEvent) => { + event.preventDefault() + event.stopPropagation() + if (!props.showDeleteMessage) return + if (!props.onDeleteMessagesUpTo) return + if (deletingUpTo()) return + + setDeletingUpTo(true) + try { + await props.onDeleteMessagesUpTo(props.messageId) + } finally { + setDeletingUpTo(false) + } + } + return (
@@ -1165,6 +1305,19 @@ function ReasoningCard(props: ReasoningCardProps) { + + diff --git a/packages/ui/src/components/message-item.tsx b/packages/ui/src/components/message-item.tsx index 4594edfe..4aec64ca 100644 --- a/packages/ui/src/components/message-item.tsx +++ b/packages/ui/src/components/message-item.tsx @@ -1,5 +1,5 @@ import { For, Show, createSignal } from "solid-js" -import { Copy, Split, Trash2, Undo } from "lucide-solid" +import { Copy, ListStart, Split, Trash, Undo } from "lucide-solid" import type { MessageInfo, ClientPart, SDKAssistantMessageV2 } from "../types/message" import { partHasRenderableText } from "../types/message" import type { MessageRecord } from "../stores/message-v2/types" @@ -11,6 +11,14 @@ import { deleteMessage } from "../stores/session-actions" import { isTauriHost } from "../lib/runtime-env" import type { DeleteHoverState } from "../types/delete-hover" +function DeleteUpToIcon() { + return ( + + ) +} + interface MessageItemProps { record: MessageRecord messageInfo?: MessageInfo @@ -19,6 +27,7 @@ interface MessageItemProps { isQueued?: boolean parts: ClientPart[] onRevert?: (messageId: string) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise onFork?: (messageId?: string) => void showAgentMeta?: boolean onContentRendered?: () => void @@ -30,6 +39,7 @@ export default function MessageItem(props: MessageItemProps) { const { t } = useI18n() const [copied, setCopied] = createSignal(false) const [deletingMessage, setDeletingMessage] = createSignal(false) + const [deletingUpTo, setDeletingUpTo] = createSignal(false) const isUser = () => props.record.role === "user" const createdTimestamp = () => props.messageInfo?.time?.created ?? props.record.createdAt @@ -209,6 +219,17 @@ export default function MessageItem(props: MessageItemProps) { } } + const handleDeleteUpTo = async () => { + if (!props.onDeleteMessagesUpTo) return + if (deletingUpTo()) return + setDeletingUpTo(true) + try { + await props.onDeleteMessagesUpTo(props.record.id) + } finally { + setDeletingUpTo(false) + } + } + if (!isUser() && !hasContent() && !isGenerating()) { return null } @@ -305,6 +326,18 @@ export default function MessageItem(props: MessageItemProps) { + +
@@ -331,6 +364,18 @@ export default function MessageItem(props: MessageItemProps) { + +
diff --git a/packages/ui/src/components/message-preview.tsx b/packages/ui/src/components/message-preview.tsx index bb8aa1be..9c1a37ba 100644 --- a/packages/ui/src/components/message-preview.tsx +++ b/packages/ui/src/components/message-preview.tsx @@ -10,6 +10,7 @@ interface MessagePreviewProps { store: () => InstanceMessageStore deleteHover?: () => DeleteHoverState onDeleteHoverChange?: (state: DeleteHoverState) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise } const MessagePreview: Component = (props) => { @@ -29,6 +30,7 @@ const MessagePreview: Component = (props) => { showUsageMetrics={() => false} deleteHover={props.deleteHover} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} />
) diff --git a/packages/ui/src/components/message-section.tsx b/packages/ui/src/components/message-section.tsx index a0c00c09..e062a219 100644 --- a/packages/ui/src/components/message-section.tsx +++ b/packages/ui/src/components/message-section.tsx @@ -24,6 +24,7 @@ export interface MessageSectionProps { sessionId: string loading?: boolean onRevert?: (messageId: string) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise onFork?: (messageId?: string) => void registerScrollToBottom?: (fn: () => void) => void showSidebarToggle?: boolean @@ -900,6 +901,7 @@ export default function MessageSection(props: MessageSectionProps) { scrollContainer={scrollElement} loading={props.loading} onRevert={props.onRevert} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} onFork={props.onFork} onContentRendered={handleContentRendered} deleteHover={deleteHover} @@ -964,6 +966,7 @@ export default function MessageSection(props: MessageSectionProps) { showToolSegments={showTimelineToolsPreference()} deleteHover={deleteHover} onDeleteHoverChange={setDeleteHover} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} />
diff --git a/packages/ui/src/components/message-timeline.tsx b/packages/ui/src/components/message-timeline.tsx index 59348878..7549a926 100644 --- a/packages/ui/src/components/message-timeline.tsx +++ b/packages/ui/src/components/message-timeline.tsx @@ -33,6 +33,7 @@ interface MessageTimelineProps { showToolSegments?: boolean deleteHover?: () => DeleteHoverState onDeleteHoverChange?: (state: DeleteHoverState) => void + onDeleteMessagesUpTo?: (messageId: string) => void | Promise } const MAX_TOOLTIP_LENGTH = 220 @@ -419,6 +420,16 @@ const MessageTimeline: Component = (props) => { if (hover.kind === "message") { return hover.messageId === segment.messageId } + + if (hover.kind === "deleteUpTo") { + const ids = store().getSessionMessageIds(props.sessionId) + const targetIndex = ids.indexOf(hover.messageId) + if (targetIndex === -1) return false + const segmentIndex = ids.indexOf(segment.messageId) + if (segmentIndex === -1) return false + return segmentIndex >= targetIndex + } + return false } @@ -490,6 +501,7 @@ const MessageTimeline: Component = (props) => { store={store} deleteHover={props.deleteHover} onDeleteHoverChange={props.onDeleteHoverChange} + onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} /> ) diff --git a/packages/ui/src/components/session/session-view.tsx b/packages/ui/src/components/session/session-view.tsx index 58adfede..cc58aa0a 100644 --- a/packages/ui/src/components/session/session-view.tsx +++ b/packages/ui/src/components/session/session-view.tsx @@ -10,6 +10,7 @@ import { getAttachments, removeAttachment } from "../../stores/attachments" import { instances } from "../../stores/instances" import { loadMessages, sendMessage, forkSession, renameSession, isSessionMessagesLoading, setActiveParentSession, setActiveSession, runShellCommand, abortSession } from "../../stores/sessions" import { isSessionBusy as getSessionBusyStatus } from "../../stores/session-status" +import { deleteMessage } from "../../stores/session-actions" import { showAlertDialog } from "../../stores/alerts" import { getLogger } from "../../lib/logger" import { requestData } from "../../lib/opencode-api" @@ -225,6 +226,35 @@ export const SessionView: Component = (props) => { } } + async function handleDeleteMessagesUpTo(messageId: string) { + const ids = messageStore().getSessionMessageIds(props.sessionId) + const index = ids.indexOf(messageId) + if (index === -1) return + + const restoredText = getUserMessageText(messageId) + const toDelete = ids.slice(index) + + try { + for (let idx = toDelete.length - 1; idx >= 0; idx -= 1) { + await deleteMessage(props.instanceId, props.sessionId, toDelete[idx]) + } + } catch (error) { + log.error("Failed to delete messages up to", error) + showAlertDialog(t("sessionView.alerts.deleteUpToFailed.message"), { + title: t("sessionView.alerts.deleteUpToFailed.title"), + variant: "error", + }) + } finally { + if (restoredText) { + if (promptInputApi) { + promptInputApi.setPromptText(restoredText, { focus: true }) + } else { + pendingPromptText = restoredText + } + } + } + } + async function handleFork(messageId?: string) { if (!messageId) { log.warn("Fork requires a user message id") @@ -283,10 +313,11 @@ export const SessionView: Component = (props) => { { scrollToBottomHandle = fn if (props.isActive) { diff --git a/packages/ui/src/lib/i18n/messages/en/messaging.ts b/packages/ui/src/lib/i18n/messages/en/messaging.ts index baa96213..a6dc2c6c 100644 --- a/packages/ui/src/lib/i18n/messages/en/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/en/messaging.ts @@ -71,13 +71,14 @@ export const messagingMessages = { "messageItem.speaker.you": "You", "messageItem.speaker.assistant": "Assistant", "messageItem.actions.revert": "Revert", - "messageItem.actions.revertTitle": "Undo changes up to here", + "messageItem.actions.revertTitle": "Undo changes up to here (deletes messages)", "messageItem.actions.fork": "Fork", "messageItem.actions.forkTitle": "Fork from this message", "messageItem.actions.copy": "Copy", "messageItem.actions.copyTitle": "Copy message", "messageItem.actions.copied": "Copied!", - "messageItem.actions.deleteMessage": "Delete message", + "messageItem.actions.deleteMessage": "Delete message (doesn't undo changes)", + "messageItem.actions.deleteMessagesUpTo": "Delete messages up to here (doesn't undo changes)", "messageItem.actions.deletingMessage": "Deleting...", "messageItem.actions.deleteMessageFailedTitle": "Delete failed", "messageItem.actions.deleteMessageFailedMessage": "Failed to delete message", diff --git a/packages/ui/src/lib/i18n/messages/en/session.ts b/packages/ui/src/lib/i18n/messages/en/session.ts index 699c50d6..d5db91b0 100644 --- a/packages/ui/src/lib/i18n/messages/en/session.ts +++ b/packages/ui/src/lib/i18n/messages/en/session.ts @@ -67,6 +67,8 @@ export const sessionMessages = { "sessionView.alerts.abortFailed.title": "Stop failed", "sessionView.alerts.revertFailed.message": "Failed to revert to message", "sessionView.alerts.revertFailed.title": "Revert failed", + "sessionView.alerts.deleteUpToFailed.message": "Failed to delete messages", + "sessionView.alerts.deleteUpToFailed.title": "Delete failed", "sessionView.alerts.forkFailed.message": "Failed to fork session", "sessionView.alerts.forkFailed.title": "Fork failed", "sessionView.attachments.expandPastedTextAriaLabel": "Expand pasted text", diff --git a/packages/ui/src/lib/i18n/messages/es/messaging.ts b/packages/ui/src/lib/i18n/messages/es/messaging.ts index d31f9eb4..e617123d 100644 --- a/packages/ui/src/lib/i18n/messages/es/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/es/messaging.ts @@ -71,13 +71,14 @@ export const messagingMessages = { "messageItem.speaker.you": "Tú", "messageItem.speaker.assistant": "Asistente", "messageItem.actions.revert": "Revertir", - "messageItem.actions.revertTitle": "Deshacer cambios hasta aqui", + "messageItem.actions.revertTitle": "Deshacer cambios hasta aqui (elimina mensajes)", "messageItem.actions.fork": "Fork", "messageItem.actions.forkTitle": "Fork desde este mensaje", "messageItem.actions.copy": "Copiar", "messageItem.actions.copyTitle": "Copiar mensaje", "messageItem.actions.copied": "¡Copiado!", - "messageItem.actions.deleteMessage": "Eliminar mensaje", + "messageItem.actions.deleteMessage": "Eliminar mensaje (no deshace cambios)", + "messageItem.actions.deleteMessagesUpTo": "Eliminar mensajes hasta aqui (no deshace cambios)", "messageItem.actions.deletingMessage": "Eliminando...", "messageItem.actions.deleteMessageFailedTitle": "Error al eliminar", "messageItem.actions.deleteMessageFailedMessage": "No se pudo eliminar el mensaje", diff --git a/packages/ui/src/lib/i18n/messages/es/session.ts b/packages/ui/src/lib/i18n/messages/es/session.ts index 8c9dc7b4..313bcadb 100644 --- a/packages/ui/src/lib/i18n/messages/es/session.ts +++ b/packages/ui/src/lib/i18n/messages/es/session.ts @@ -67,6 +67,8 @@ export const sessionMessages = { "sessionView.alerts.abortFailed.title": "No se pudo detener", "sessionView.alerts.revertFailed.message": "No se pudo revertir al mensaje", "sessionView.alerts.revertFailed.title": "No se pudo revertir", + "sessionView.alerts.deleteUpToFailed.message": "No se pudieron eliminar los mensajes", + "sessionView.alerts.deleteUpToFailed.title": "Error al eliminar", "sessionView.alerts.forkFailed.message": "No se pudo hacer fork de la sesión", "sessionView.alerts.forkFailed.title": "No se pudo hacer fork", "sessionView.attachments.expandPastedTextAriaLabel": "Expandir texto pegado", diff --git a/packages/ui/src/lib/i18n/messages/fr/messaging.ts b/packages/ui/src/lib/i18n/messages/fr/messaging.ts index 9161fda5..83be6cd1 100644 --- a/packages/ui/src/lib/i18n/messages/fr/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/fr/messaging.ts @@ -71,13 +71,14 @@ export const messagingMessages = { "messageItem.speaker.you": "Vous", "messageItem.speaker.assistant": "Assistant", "messageItem.actions.revert": "Revenir", - "messageItem.actions.revertTitle": "Annuler les changements jusqu'ici", + "messageItem.actions.revertTitle": "Annuler les changements jusqu'ici (supprime les messages)", "messageItem.actions.fork": "Fork", "messageItem.actions.forkTitle": "Fork depuis ce message", "messageItem.actions.copy": "Copier", "messageItem.actions.copyTitle": "Copier le message", "messageItem.actions.copied": "Copié !", - "messageItem.actions.deleteMessage": "Supprimer le message", + "messageItem.actions.deleteMessage": "Supprimer le message (sans annuler les changements)", + "messageItem.actions.deleteMessagesUpTo": "Supprimer les messages jusqu'ici (sans annuler les changements)", "messageItem.actions.deletingMessage": "Suppression...", "messageItem.actions.deleteMessageFailedTitle": "Échec de suppression", "messageItem.actions.deleteMessageFailedMessage": "Impossible de supprimer le message", diff --git a/packages/ui/src/lib/i18n/messages/fr/session.ts b/packages/ui/src/lib/i18n/messages/fr/session.ts index 64baf1c8..96c13c30 100644 --- a/packages/ui/src/lib/i18n/messages/fr/session.ts +++ b/packages/ui/src/lib/i18n/messages/fr/session.ts @@ -67,6 +67,8 @@ export const sessionMessages = { "sessionView.alerts.abortFailed.title": "Échec de l'arrêt", "sessionView.alerts.revertFailed.message": "Impossible de revenir au message", "sessionView.alerts.revertFailed.title": "Échec du retour", + "sessionView.alerts.deleteUpToFailed.message": "Impossible de supprimer les messages", + "sessionView.alerts.deleteUpToFailed.title": "Échec de suppression", "sessionView.alerts.forkFailed.message": "Impossible de forker la session", "sessionView.alerts.forkFailed.title": "Échec du fork", "sessionView.attachments.expandPastedTextAriaLabel": "Développer le texte collé", diff --git a/packages/ui/src/lib/i18n/messages/ja/messaging.ts b/packages/ui/src/lib/i18n/messages/ja/messaging.ts index 99f602ec..51cabb9d 100644 --- a/packages/ui/src/lib/i18n/messages/ja/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/ja/messaging.ts @@ -71,13 +71,14 @@ export const messagingMessages = { "messageItem.speaker.you": "あなた", "messageItem.speaker.assistant": "アシスタント", "messageItem.actions.revert": "戻す", - "messageItem.actions.revertTitle": "ここまでの変更を元に戻す", + "messageItem.actions.revertTitle": "ここまでの変更を元に戻す(メッセージを削除)", "messageItem.actions.fork": "フォーク", "messageItem.actions.forkTitle": "このメッセージからフォーク", "messageItem.actions.copy": "コピー", "messageItem.actions.copyTitle": "メッセージをコピー", "messageItem.actions.copied": "コピーしました!", - "messageItem.actions.deleteMessage": "メッセージを削除", + "messageItem.actions.deleteMessage": "メッセージを削除(変更は元に戻さない)", + "messageItem.actions.deleteMessagesUpTo": "ここまでのメッセージを削除(変更は元に戻さない)", "messageItem.actions.deletingMessage": "削除中...", "messageItem.actions.deleteMessageFailedTitle": "削除に失敗しました", "messageItem.actions.deleteMessageFailedMessage": "メッセージの削除に失敗しました", diff --git a/packages/ui/src/lib/i18n/messages/ja/session.ts b/packages/ui/src/lib/i18n/messages/ja/session.ts index ae7e60a2..c7e33291 100644 --- a/packages/ui/src/lib/i18n/messages/ja/session.ts +++ b/packages/ui/src/lib/i18n/messages/ja/session.ts @@ -67,6 +67,8 @@ export const sessionMessages = { "sessionView.alerts.abortFailed.title": "停止に失敗", "sessionView.alerts.revertFailed.message": "メッセージへ戻せませんでした", "sessionView.alerts.revertFailed.title": "復元に失敗", + "sessionView.alerts.deleteUpToFailed.message": "メッセージの削除に失敗しました", + "sessionView.alerts.deleteUpToFailed.title": "削除に失敗しました", "sessionView.alerts.forkFailed.message": "セッションのフォークに失敗しました", "sessionView.alerts.forkFailed.title": "フォークに失敗", "sessionView.attachments.expandPastedTextAriaLabel": "貼り付けたテキストを展開", diff --git a/packages/ui/src/lib/i18n/messages/ru/messaging.ts b/packages/ui/src/lib/i18n/messages/ru/messaging.ts index bb52fcb9..a6d47780 100644 --- a/packages/ui/src/lib/i18n/messages/ru/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/ru/messaging.ts @@ -71,13 +71,14 @@ export const messagingMessages = { "messageItem.speaker.you": "Вы", "messageItem.speaker.assistant": "Ассистент", "messageItem.actions.revert": "Откатить", - "messageItem.actions.revertTitle": "Отменить изменения до этого места", + "messageItem.actions.revertTitle": "Отменить изменения до этого места (удалит сообщения)", "messageItem.actions.fork": "Форк", "messageItem.actions.forkTitle": "Форкнуть от этого сообщения", "messageItem.actions.copy": "Копировать", "messageItem.actions.copyTitle": "Копировать сообщение", "messageItem.actions.copied": "Скопировано!", - "messageItem.actions.deleteMessage": "Удалить сообщение", + "messageItem.actions.deleteMessage": "Удалить сообщение (без отката изменений)", + "messageItem.actions.deleteMessagesUpTo": "Удалить сообщения до этого места (без отката изменений)", "messageItem.actions.deletingMessage": "Удаление...", "messageItem.actions.deleteMessageFailedTitle": "Ошибка удаления", "messageItem.actions.deleteMessageFailedMessage": "Не удалось удалить сообщение", diff --git a/packages/ui/src/lib/i18n/messages/ru/session.ts b/packages/ui/src/lib/i18n/messages/ru/session.ts index 8d348183..5a17fd7e 100644 --- a/packages/ui/src/lib/i18n/messages/ru/session.ts +++ b/packages/ui/src/lib/i18n/messages/ru/session.ts @@ -67,6 +67,8 @@ export const sessionMessages = { "sessionView.alerts.abortFailed.title": "Не удалось остановить", "sessionView.alerts.revertFailed.message": "Не удалось откатиться к сообщению", "sessionView.alerts.revertFailed.title": "Не удалось откатиться", + "sessionView.alerts.deleteUpToFailed.message": "Не удалось удалить сообщения", + "sessionView.alerts.deleteUpToFailed.title": "Ошибка удаления", "sessionView.alerts.forkFailed.message": "Не удалось форкнуть сессию", "sessionView.alerts.forkFailed.title": "Не удалось форкнуть", "sessionView.attachments.expandPastedTextAriaLabel": "Развернуть вставленный текст", diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts index e446002f..3cf2fa24 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts @@ -71,13 +71,14 @@ export const messagingMessages = { "messageItem.speaker.you": "你", "messageItem.speaker.assistant": "助手", "messageItem.actions.revert": "回退", - "messageItem.actions.revertTitle": "撤销到此处的更改", + "messageItem.actions.revertTitle": "撤销到此处的更改(会删除消息)", "messageItem.actions.fork": "分叉", "messageItem.actions.forkTitle": "从这条消息分叉", "messageItem.actions.copy": "复制", "messageItem.actions.copyTitle": "复制消息", "messageItem.actions.copied": "已复制!", - "messageItem.actions.deleteMessage": "删除消息", + "messageItem.actions.deleteMessage": "删除消息(不会撤销更改)", + "messageItem.actions.deleteMessagesUpTo": "删除到此处的消息(不会撤销更改)", "messageItem.actions.deletingMessage": "正在删除...", "messageItem.actions.deleteMessageFailedTitle": "删除失败", "messageItem.actions.deleteMessageFailedMessage": "无法删除消息", diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/session.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/session.ts index c9ebb0d3..d58554e1 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/session.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/session.ts @@ -67,6 +67,8 @@ export const sessionMessages = { "sessionView.alerts.abortFailed.title": "停止失败", "sessionView.alerts.revertFailed.message": "回退到消息失败", "sessionView.alerts.revertFailed.title": "回退失败", + "sessionView.alerts.deleteUpToFailed.message": "无法删除消息", + "sessionView.alerts.deleteUpToFailed.title": "删除失败", "sessionView.alerts.forkFailed.message": "分叉会话失败", "sessionView.alerts.forkFailed.title": "分叉失败", "sessionView.attachments.expandPastedTextAriaLabel": "展开粘贴的文本", diff --git a/packages/ui/src/styles/messaging/delete-overlays.css b/packages/ui/src/styles/messaging/delete-overlays.css index 4ccb899f..344c8a1e 100644 --- a/packages/ui/src/styles/messaging/delete-overlays.css +++ b/packages/ui/src/styles/messaging/delete-overlays.css @@ -9,7 +9,6 @@ position: absolute; inset: -2px; background: var(--status-error-bg); - box-shadow: inset 0 0 0 1px var(--status-error-fg); border-radius: 0; pointer-events: none; /* Overlay must sit above the message cards (they have opaque backgrounds). */ @@ -29,7 +28,6 @@ position: absolute; inset: -2px; background: var(--status-error-bg); - box-shadow: inset 0 0 0 1px var(--status-error-fg); border-radius: 0; pointer-events: none; /* Overlay must sit above the part card background. */ diff --git a/packages/ui/src/styles/messaging/message-timeline.css b/packages/ui/src/styles/messaging/message-timeline.css index f11c5ce9..7e96b160 100644 --- a/packages/ui/src/styles/messaging/message-timeline.css +++ b/packages/ui/src/styles/messaging/message-timeline.css @@ -104,7 +104,6 @@ position: absolute; inset: 0; background: var(--status-error-bg); - box-shadow: inset 0 0 0 1px var(--status-error-fg); border-radius: 0; pointer-events: none; z-index: 2; diff --git a/packages/ui/src/types/delete-hover.ts b/packages/ui/src/types/delete-hover.ts index 3f69e57d..2f67c08f 100644 --- a/packages/ui/src/types/delete-hover.ts +++ b/packages/ui/src/types/delete-hover.ts @@ -1,3 +1,4 @@ export type DeleteHoverState = | { kind: "none" } | { kind: "message"; messageId: string } + | { kind: "deleteUpTo"; messageId: string }