feat(ui): add delete message action to stream
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, untrack } from "solid-js"
|
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, MessageSquareX, Trash2 } from "lucide-solid"
|
||||||
import MessageItem from "./message-item"
|
import MessageItem from "./message-item"
|
||||||
import ToolCall from "./tool-call"
|
import ToolCall from "./tool-call"
|
||||||
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
|
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
|
||||||
@@ -13,6 +13,7 @@ import { sessions, setActiveParentSession, setActiveSession } from "../stores/se
|
|||||||
import { setActiveInstanceId } from "../stores/instances"
|
import { setActiveInstanceId } from "../stores/instances"
|
||||||
import { showAlertDialog } from "../stores/alerts"
|
import { showAlertDialog } from "../stores/alerts"
|
||||||
import { deleteMessagePart } from "../stores/session-actions"
|
import { deleteMessagePart } from "../stores/session-actions"
|
||||||
|
import { deleteMessage } from "../stores/session-actions"
|
||||||
import { useI18n } from "../lib/i18n"
|
import { useI18n } from "../lib/i18n"
|
||||||
|
|
||||||
const TOOL_ICON = "🔧"
|
const TOOL_ICON = "🔧"
|
||||||
@@ -196,6 +197,7 @@ interface MessageContentItemProps {
|
|||||||
onRevert?: (messageId: string) => void
|
onRevert?: (messageId: string) => void
|
||||||
onFork?: (messageId?: string) => void
|
onFork?: (messageId?: string) => void
|
||||||
onContentRendered?: () => void
|
onContentRendered?: () => void
|
||||||
|
showDeleteMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSupportedPartType(part: unknown): boolean {
|
function isSupportedPartType(part: unknown): boolean {
|
||||||
@@ -282,6 +284,7 @@ function MessageContentItem(props: MessageContentItemProps) {
|
|||||||
sessionId={props.sessionId}
|
sessionId={props.sessionId}
|
||||||
isQueued={isQueued()}
|
isQueued={isQueued()}
|
||||||
showAgentMeta={showAgentMeta()}
|
showAgentMeta={showAgentMeta()}
|
||||||
|
showDeleteMessage={props.showDeleteMessage}
|
||||||
onRevert={props.onRevert}
|
onRevert={props.onRevert}
|
||||||
onFork={props.onFork}
|
onFork={props.onFork}
|
||||||
onContentRendered={props.onContentRendered}
|
onContentRendered={props.onContentRendered}
|
||||||
@@ -298,11 +301,13 @@ interface ToolCallItemProps {
|
|||||||
messageId: string
|
messageId: string
|
||||||
partId: string
|
partId: string
|
||||||
onContentRendered?: () => void
|
onContentRendered?: () => void
|
||||||
|
showDeleteMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolCallItem(props: ToolCallItemProps) {
|
function ToolCallItem(props: ToolCallItemProps) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const [deleting, setDeleting] = createSignal(false)
|
const [deleting, setDeleting] = createSignal(false)
|
||||||
|
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||||
|
|
||||||
const record = createMemo(() => props.store().getMessage(props.messageId))
|
const record = createMemo(() => props.store().getMessage(props.messageId))
|
||||||
const messageInfo = createMemo(() => props.store().getMessageInfo(props.messageId))
|
const messageInfo = createMemo(() => props.store().getMessageInfo(props.messageId))
|
||||||
@@ -370,6 +375,27 @@ function ToolCallItem(props: ToolCallItemProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDeleteMessage = async (event: MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
if (!props.showDeleteMessage) return
|
||||||
|
if (deletingMessage()) return
|
||||||
|
|
||||||
|
setDeletingMessage(true)
|
||||||
|
try {
|
||||||
|
await deleteMessage(props.instanceId, props.sessionId, props.messageId)
|
||||||
|
} catch (error) {
|
||||||
|
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
|
||||||
|
title: t("messageItem.actions.deleteMessageFailedTitle"),
|
||||||
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setDeletingMessage(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={toolPart()}>
|
<Show when={toolPart()}>
|
||||||
{(resolvedToolPart) => (
|
{(resolvedToolPart) => (
|
||||||
@@ -381,7 +407,7 @@ function ToolCallItem(props: ToolCallItemProps) {
|
|||||||
<span class="tool-name">{toolName() || t("messageBlock.tool.unknown")}</span>
|
<span class="tool-name">{toolName() || t("messageBlock.tool.unknown")}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Show when={taskSessionId()}>
|
<Show when={taskSessionId()}>
|
||||||
<button
|
<button
|
||||||
class="tool-call-header-button"
|
class="tool-call-header-button"
|
||||||
@@ -395,18 +421,31 @@ function ToolCallItem(props: ToolCallItemProps) {
|
|||||||
</button>
|
</button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="tool-call-header-button"
|
class="tool-call-header-button"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={deleteDisabled()}
|
disabled={deleteDisabled()}
|
||||||
onClick={handleDeleteToolPart}
|
onClick={handleDeleteToolPart}
|
||||||
title={deleting() ? t("messageBlock.tool.deletePart.deleting") : t("messageBlock.tool.deletePart.label")}
|
title={deleting() ? t("messageBlock.tool.deletePart.deleting") : t("messageBlock.tool.deletePart.label")}
|
||||||
aria-label={deleting() ? t("messageBlock.tool.deletePart.deleting") : t("messageBlock.tool.deletePart.label")}
|
aria-label={deleting() ? t("messageBlock.tool.deletePart.deleting") : t("messageBlock.tool.deletePart.label")}
|
||||||
>
|
>
|
||||||
<Trash2 class="w-3.5 h-3.5" aria-hidden="true" />
|
<Trash2 class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<Show when={props.showDeleteMessage}>
|
||||||
|
<button
|
||||||
|
class="tool-call-header-button"
|
||||||
|
type="button"
|
||||||
|
disabled={deletingMessage()}
|
||||||
|
onClick={handleDeleteMessage}
|
||||||
|
title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
>
|
||||||
|
<MessageSquareX class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<ToolCall
|
<ToolCall
|
||||||
toolCall={resolvedToolPart()}
|
toolCall={resolvedToolPart()}
|
||||||
@@ -670,7 +709,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
{(resolvedBlock) => (
|
{(resolvedBlock) => (
|
||||||
<div class="message-stream-block" data-message-id={resolvedBlock().record.id}>
|
<div class="message-stream-block" data-message-id={resolvedBlock().record.id}>
|
||||||
<For each={resolvedBlock().items}>
|
<For each={resolvedBlock().items}>
|
||||||
{(item) => (
|
{(item, index) => (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={item.type === "content"}>
|
<Match when={item.type === "content"}>
|
||||||
<MessageContentItem
|
<MessageContentItem
|
||||||
@@ -681,6 +720,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
startPartId={(item as ContentDisplayItem).startPartId}
|
startPartId={(item as ContentDisplayItem).startPartId}
|
||||||
messageIndex={props.messageIndex}
|
messageIndex={props.messageIndex}
|
||||||
lastAssistantIndex={props.lastAssistantIndex}
|
lastAssistantIndex={props.lastAssistantIndex}
|
||||||
|
showDeleteMessage={index() === 0}
|
||||||
onRevert={props.onRevert}
|
onRevert={props.onRevert}
|
||||||
onFork={props.onFork}
|
onFork={props.onFork}
|
||||||
onContentRendered={props.onContentRendered}
|
onContentRendered={props.onContentRendered}
|
||||||
@@ -697,6 +737,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
store={props.store}
|
store={props.store}
|
||||||
messageId={toolItem.messageId}
|
messageId={toolItem.messageId}
|
||||||
partId={toolItem.partId}
|
partId={toolItem.partId}
|
||||||
|
showDeleteMessage={index() === 0}
|
||||||
onContentRendered={props.onContentRendered}
|
onContentRendered={props.onContentRendered}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -709,6 +750,10 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
part={(item as StepDisplayItem).part}
|
part={(item as StepDisplayItem).part}
|
||||||
messageInfo={(item as StepDisplayItem).messageInfo}
|
messageInfo={(item as StepDisplayItem).messageInfo}
|
||||||
showAgentMeta
|
showAgentMeta
|
||||||
|
showDeleteMessage={index() === 0}
|
||||||
|
instanceId={props.instanceId}
|
||||||
|
sessionId={props.sessionId}
|
||||||
|
messageId={props.messageId}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={item.type === "step-finish"}>
|
<Match when={item.type === "step-finish"}>
|
||||||
@@ -718,6 +763,10 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
messageInfo={(item as StepDisplayItem).messageInfo}
|
messageInfo={(item as StepDisplayItem).messageInfo}
|
||||||
showUsage={props.showUsageMetrics()}
|
showUsage={props.showUsageMetrics()}
|
||||||
borderColor={(item as StepDisplayItem).accentColor}
|
borderColor={(item as StepDisplayItem).accentColor}
|
||||||
|
showDeleteMessage={index() === 0}
|
||||||
|
instanceId={props.instanceId}
|
||||||
|
sessionId={props.sessionId}
|
||||||
|
messageId={props.messageId}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={item.type === "compaction"}>
|
<Match when={item.type === "compaction"}>
|
||||||
@@ -729,6 +778,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
sessionId={props.sessionId}
|
sessionId={props.sessionId}
|
||||||
messageId={(item as CompactionDisplayItem).messageId}
|
messageId={(item as CompactionDisplayItem).messageId}
|
||||||
partId={(item as CompactionDisplayItem).partId}
|
partId={(item as CompactionDisplayItem).partId}
|
||||||
|
showDeleteMessage={index() === 0}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={item.type === "reasoning"}>
|
<Match when={item.type === "reasoning"}>
|
||||||
@@ -741,6 +791,7 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
partId={(item as ReasoningDisplayItem).partId}
|
partId={(item as ReasoningDisplayItem).partId}
|
||||||
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
|
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
|
||||||
defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
|
defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
|
||||||
|
showDeleteMessage={index() === 0}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -759,6 +810,10 @@ interface StepCardProps {
|
|||||||
showAgentMeta?: boolean
|
showAgentMeta?: boolean
|
||||||
showUsage?: boolean
|
showUsage?: boolean
|
||||||
borderColor?: string
|
borderColor?: string
|
||||||
|
showDeleteMessage?: boolean
|
||||||
|
instanceId?: string
|
||||||
|
sessionId?: string
|
||||||
|
messageId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompactionCardProps {
|
interface CompactionCardProps {
|
||||||
@@ -769,11 +824,13 @@ interface CompactionCardProps {
|
|||||||
sessionId: string
|
sessionId: string
|
||||||
messageId: string
|
messageId: string
|
||||||
partId: string
|
partId: string
|
||||||
|
showDeleteMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function CompactionCard(props: CompactionCardProps) {
|
function CompactionCard(props: CompactionCardProps) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const [deleting, setDeleting] = createSignal(false)
|
const [deleting, setDeleting] = createSignal(false)
|
||||||
|
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||||
const isAuto = () => Boolean((props.part as any)?.auto)
|
const isAuto = () => Boolean((props.part as any)?.auto)
|
||||||
const label = () => (isAuto() ? t("messageBlock.compaction.autoLabel") : t("messageBlock.compaction.manualLabel"))
|
const label = () => (isAuto() ? t("messageBlock.compaction.autoLabel") : t("messageBlock.compaction.manualLabel"))
|
||||||
const borderColor = () => props.borderColor ?? (isAuto() ? "var(--session-status-compacting-fg)" : USER_BORDER_COLOR)
|
const borderColor = () => props.borderColor ?? (isAuto() ? "var(--session-status-compacting-fg)" : USER_BORDER_COLOR)
|
||||||
@@ -801,6 +858,27 @@ function CompactionCard(props: CompactionCardProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canDeleteMessage = () => Boolean(props.showDeleteMessage) && !deletingMessage()
|
||||||
|
|
||||||
|
const handleDeleteMessage = async (event: MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
if (!props.showDeleteMessage) return
|
||||||
|
if (!canDeleteMessage()) return
|
||||||
|
setDeletingMessage(true)
|
||||||
|
try {
|
||||||
|
await deleteMessage(props.instanceId, props.sessionId, props.messageId)
|
||||||
|
} catch (error) {
|
||||||
|
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
|
||||||
|
title: t("messageItem.actions.deleteMessageFailedTitle"),
|
||||||
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setDeletingMessage(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={`${containerClass()} relative`}
|
class={`${containerClass()} relative`}
|
||||||
@@ -808,15 +886,31 @@ function CompactionCard(props: CompactionCardProps) {
|
|||||||
role="status"
|
role="status"
|
||||||
aria-label={t("messageBlock.compaction.ariaLabel")}
|
aria-label={t("messageBlock.compaction.ariaLabel")}
|
||||||
>
|
>
|
||||||
<button
|
<div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1">
|
||||||
type="button"
|
<Show when={props.showDeleteMessage}>
|
||||||
class="tool-call-header-button absolute right-2 top-1/2 -translate-y-1/2"
|
<button
|
||||||
disabled={!canDelete()}
|
type="button"
|
||||||
onClick={handleDelete}
|
class="tool-call-header-button"
|
||||||
title={t("messagePart.actions.deleteTitle")}
|
disabled={!canDeleteMessage()}
|
||||||
>
|
onClick={handleDeleteMessage}
|
||||||
{deleting() ? t("messagePart.actions.deleting") : t("messagePart.actions.delete")}
|
title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
</button>
|
aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
>
|
||||||
|
<MessageSquareX class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="tool-call-header-button"
|
||||||
|
disabled={!canDelete()}
|
||||||
|
onClick={handleDelete}
|
||||||
|
title={t("messagePart.actions.deleteTitle")}
|
||||||
|
aria-label={t("messagePart.actions.deleteTitle")}
|
||||||
|
>
|
||||||
|
{deleting() ? t("messagePart.actions.deleting") : t("messagePart.actions.delete")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="message-compaction-row">
|
<div class="message-compaction-row">
|
||||||
<FoldVertical class="message-compaction-icon w-4 h-4" aria-hidden="true" />
|
<FoldVertical class="message-compaction-icon w-4 h-4" aria-hidden="true" />
|
||||||
@@ -828,6 +922,7 @@ function CompactionCard(props: CompactionCardProps) {
|
|||||||
|
|
||||||
function StepCard(props: StepCardProps) {
|
function StepCard(props: StepCardProps) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||||
const timestamp = () => {
|
const timestamp = () => {
|
||||||
const value = props.messageInfo?.time?.created ?? (props.part as any)?.time?.start ?? Date.now()
|
const value = props.messageInfo?.time?.created ?? (props.part as any)?.time?.start ?? Date.now()
|
||||||
const date = new Date(value)
|
const date = new Date(value)
|
||||||
@@ -872,6 +967,27 @@ function StepCard(props: StepCardProps) {
|
|||||||
|
|
||||||
const finishStyle = () => (props.borderColor ? { "border-left-color": props.borderColor } : undefined)
|
const finishStyle = () => (props.borderColor ? { "border-left-color": props.borderColor } : undefined)
|
||||||
|
|
||||||
|
const canDeleteMessage = () =>
|
||||||
|
Boolean(props.showDeleteMessage && props.instanceId && props.sessionId && props.messageId) && !deletingMessage()
|
||||||
|
|
||||||
|
const handleDeleteMessage = async (event: MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
if (!canDeleteMessage()) return
|
||||||
|
setDeletingMessage(true)
|
||||||
|
try {
|
||||||
|
await deleteMessage(props.instanceId!, props.sessionId!, props.messageId!)
|
||||||
|
} catch (error) {
|
||||||
|
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
|
||||||
|
title: t("messageItem.actions.deleteMessageFailedTitle"),
|
||||||
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setDeletingMessage(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const renderUsageChips = (usage: NonNullable<ReturnType<typeof usageStats>>) => {
|
const renderUsageChips = (usage: NonNullable<ReturnType<typeof usageStats>>) => {
|
||||||
const entries = [
|
const entries = [
|
||||||
@@ -902,7 +1018,20 @@ function StepCard(props: StepCardProps) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div class={`message-step-card message-step-finish message-step-finish-flush`} style={finishStyle()}>
|
<div class={`message-step-card message-step-finish message-step-finish-flush relative`} style={finishStyle()}>
|
||||||
|
<Show when={props.showDeleteMessage}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="message-action-button absolute right-2 top-1/2 -translate-y-1/2"
|
||||||
|
disabled={!canDeleteMessage()}
|
||||||
|
onClick={handleDeleteMessage}
|
||||||
|
title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
>
|
||||||
|
<MessageSquareX class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
|
||||||
{renderUsageChips(usage)}
|
{renderUsageChips(usage)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -942,12 +1071,14 @@ interface ReasoningCardProps {
|
|||||||
partId: string
|
partId: string
|
||||||
showAgentMeta?: boolean
|
showAgentMeta?: boolean
|
||||||
defaultExpanded?: boolean
|
defaultExpanded?: boolean
|
||||||
|
showDeleteMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReasoningCard(props: ReasoningCardProps) {
|
function ReasoningCard(props: ReasoningCardProps) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const [expanded, setExpanded] = createSignal(Boolean(props.defaultExpanded))
|
const [expanded, setExpanded] = createSignal(Boolean(props.defaultExpanded))
|
||||||
const [deleting, setDeleting] = createSignal(false)
|
const [deleting, setDeleting] = createSignal(false)
|
||||||
|
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
setExpanded(Boolean(props.defaultExpanded))
|
setExpanded(Boolean(props.defaultExpanded))
|
||||||
@@ -1035,6 +1166,27 @@ function ReasoningCard(props: ReasoningCardProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canDeleteMessage = () => Boolean(props.showDeleteMessage) && !deletingMessage()
|
||||||
|
|
||||||
|
const handleDeleteMessage = async (event: MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
if (!props.showDeleteMessage) return
|
||||||
|
if (!canDeleteMessage()) return
|
||||||
|
setDeletingMessage(true)
|
||||||
|
try {
|
||||||
|
await deleteMessage(props.instanceId, props.sessionId, props.messageId)
|
||||||
|
} catch (error) {
|
||||||
|
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
|
||||||
|
title: t("messageItem.actions.deleteMessageFailedTitle"),
|
||||||
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setDeletingMessage(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="message-reasoning-card">
|
<div class="message-reasoning-card">
|
||||||
<div class="message-reasoning-header">
|
<div class="message-reasoning-header">
|
||||||
@@ -1094,6 +1246,19 @@ function ReasoningCard(props: ReasoningCardProps) {
|
|||||||
</button>
|
</button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.showDeleteMessage}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="message-action-button"
|
||||||
|
onClick={handleDeleteMessage}
|
||||||
|
disabled={!canDeleteMessage()}
|
||||||
|
aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
>
|
||||||
|
<MessageSquareX class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<span class="message-reasoning-time">{timestamp()}</span>
|
<span class="message-reasoning-time">{timestamp()}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { For, Show, createSignal } from "solid-js"
|
import { For, Show, createSignal } from "solid-js"
|
||||||
import { Copy, ExternalLink, Split, Trash2, Undo } from "lucide-solid"
|
import { Copy, MessageSquareX, Split, Trash2, Undo } from "lucide-solid"
|
||||||
import type { MessageInfo, ClientPart, SDKAssistantMessageV2 } from "../types/message"
|
import type { MessageInfo, ClientPart, SDKAssistantMessageV2 } from "../types/message"
|
||||||
import { partHasRenderableText } from "../types/message"
|
import { partHasRenderableText } from "../types/message"
|
||||||
import type { MessageRecord } from "../stores/message-v2/types"
|
import type { MessageRecord } from "../stores/message-v2/types"
|
||||||
@@ -7,7 +7,7 @@ import MessagePart from "./message-part"
|
|||||||
import { copyToClipboard } from "../lib/clipboard"
|
import { copyToClipboard } from "../lib/clipboard"
|
||||||
import { useI18n } from "../lib/i18n"
|
import { useI18n } from "../lib/i18n"
|
||||||
import { showAlertDialog } from "../stores/alerts"
|
import { showAlertDialog } from "../stores/alerts"
|
||||||
import { deleteMessagePart } from "../stores/session-actions"
|
import { deleteMessage, deleteMessagePart } from "../stores/session-actions"
|
||||||
import { isTauriHost } from "../lib/runtime-env"
|
import { isTauriHost } from "../lib/runtime-env"
|
||||||
|
|
||||||
interface MessageItemProps {
|
interface MessageItemProps {
|
||||||
@@ -21,12 +21,14 @@ interface MessageItemProps {
|
|||||||
onFork?: (messageId?: string) => void
|
onFork?: (messageId?: string) => void
|
||||||
showAgentMeta?: boolean
|
showAgentMeta?: boolean
|
||||||
onContentRendered?: () => void
|
onContentRendered?: () => void
|
||||||
|
showDeleteMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MessageItem(props: MessageItemProps) {
|
export default function MessageItem(props: MessageItemProps) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const [copied, setCopied] = createSignal(false)
|
const [copied, setCopied] = createSignal(false)
|
||||||
const [deletingParts, setDeletingParts] = createSignal<Set<string>>(new Set())
|
const [deletingParts, setDeletingParts] = createSignal<Set<string>>(new Set())
|
||||||
|
const [deletingMessage, setDeletingMessage] = createSignal(false)
|
||||||
|
|
||||||
const isUser = () => props.record.role === "user"
|
const isUser = () => props.record.role === "user"
|
||||||
const createdTimestamp = () => props.messageInfo?.time?.created ?? props.record.createdAt
|
const createdTimestamp = () => props.messageInfo?.time?.created ?? props.record.createdAt
|
||||||
@@ -234,6 +236,22 @@ export default function MessageItem(props: MessageItemProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDeleteMessage = async () => {
|
||||||
|
if (deletingMessage()) return
|
||||||
|
setDeletingMessage(true)
|
||||||
|
try {
|
||||||
|
await deleteMessage(props.instanceId, props.sessionId, props.record.id)
|
||||||
|
} catch (error) {
|
||||||
|
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
|
||||||
|
title: t("messageItem.actions.deleteMessageFailedTitle"),
|
||||||
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setDeletingMessage(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isUser() && !hasContent() && !isGenerating()) {
|
if (!isUser() && !hasContent() && !isGenerating()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -326,6 +344,18 @@ export default function MessageItem(props: MessageItemProps) {
|
|||||||
>
|
>
|
||||||
<Copy class="w-3.5 h-3.5" aria-hidden="true" />
|
<Copy class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<Show when={props.showDeleteMessage}>
|
||||||
|
<button
|
||||||
|
class="message-action-button"
|
||||||
|
onClick={handleDeleteMessage}
|
||||||
|
disabled={deletingMessage()}
|
||||||
|
title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
>
|
||||||
|
<MessageSquareX class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!isUser()}>
|
<Show when={!isUser()}>
|
||||||
@@ -352,6 +382,18 @@ export default function MessageItem(props: MessageItemProps) {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.showDeleteMessage}>
|
||||||
|
<button
|
||||||
|
class="message-action-button"
|
||||||
|
onClick={handleDeleteMessage}
|
||||||
|
disabled={deletingMessage()}
|
||||||
|
title={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
aria-label={deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage")}
|
||||||
|
>
|
||||||
|
<MessageSquareX class="w-3.5 h-3.5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<time class="message-timestamp" dateTime={timestampIso()}>{timestamp()}</time>
|
<time class="message-timestamp" dateTime={timestampIso()}>{timestamp()}</time>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const messagingMessages = {
|
|||||||
"messageBlock.tool.goToSession.label": "Go to Session",
|
"messageBlock.tool.goToSession.label": "Go to Session",
|
||||||
"messageBlock.tool.goToSession.title": "Go to session",
|
"messageBlock.tool.goToSession.title": "Go to session",
|
||||||
"messageBlock.tool.goToSession.unavailableTitle": "Session not available yet",
|
"messageBlock.tool.goToSession.unavailableTitle": "Session not available yet",
|
||||||
"messageBlock.tool.deletePart.label": "Delete",
|
"messageBlock.tool.deletePart.label": "Delete Part",
|
||||||
"messageBlock.tool.deletePart.deleting": "Deleting...",
|
"messageBlock.tool.deletePart.deleting": "Deleting...",
|
||||||
"messageBlock.tool.deletePart.title": "Delete this tool call output",
|
"messageBlock.tool.deletePart.title": "Delete this tool call output",
|
||||||
"messageBlock.tool.deletePart.failed.title": "Delete failed",
|
"messageBlock.tool.deletePart.failed.title": "Delete failed",
|
||||||
@@ -77,11 +77,15 @@ export const messagingMessages = {
|
|||||||
"messageItem.actions.copy": "Copy",
|
"messageItem.actions.copy": "Copy",
|
||||||
"messageItem.actions.copyTitle": "Copy message",
|
"messageItem.actions.copyTitle": "Copy message",
|
||||||
"messageItem.actions.copied": "Copied!",
|
"messageItem.actions.copied": "Copied!",
|
||||||
|
"messageItem.actions.deleteMessage": "Delete message",
|
||||||
|
"messageItem.actions.deletingMessage": "Deleting...",
|
||||||
|
"messageItem.actions.deleteMessageFailedTitle": "Delete failed",
|
||||||
|
"messageItem.actions.deleteMessageFailedMessage": "Failed to delete message",
|
||||||
"messageItem.status.queued": "QUEUED",
|
"messageItem.status.queued": "QUEUED",
|
||||||
"messageItem.status.generating": "Generating...",
|
"messageItem.status.generating": "Generating...",
|
||||||
"messageItem.status.sending": "Sending...",
|
"messageItem.status.sending": "Sending...",
|
||||||
"messageItem.status.failedToSend": "Message failed to send",
|
"messageItem.status.failedToSend": "Message failed to send",
|
||||||
"messagePart.actions.delete": "Delete",
|
"messagePart.actions.delete": "Delete Part",
|
||||||
"messagePart.actions.deleting": "Deleting...",
|
"messagePart.actions.deleting": "Deleting...",
|
||||||
"messagePart.actions.deleteTitle": "Delete this item",
|
"messagePart.actions.deleteTitle": "Delete this item",
|
||||||
"messagePart.actions.deleteFailedTitle": "Delete failed",
|
"messagePart.actions.deleteFailedTitle": "Delete failed",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const messagingMessages = {
|
|||||||
"messageBlock.tool.goToSession.label": "Ir a sesión",
|
"messageBlock.tool.goToSession.label": "Ir a sesión",
|
||||||
"messageBlock.tool.goToSession.title": "Ir a la sesión",
|
"messageBlock.tool.goToSession.title": "Ir a la sesión",
|
||||||
"messageBlock.tool.goToSession.unavailableTitle": "La sesión aún no está disponible",
|
"messageBlock.tool.goToSession.unavailableTitle": "La sesión aún no está disponible",
|
||||||
"messageBlock.tool.deletePart.label": "Eliminar",
|
"messageBlock.tool.deletePart.label": "Eliminar parte",
|
||||||
"messageBlock.tool.deletePart.deleting": "Eliminando...",
|
"messageBlock.tool.deletePart.deleting": "Eliminando...",
|
||||||
"messageBlock.tool.deletePart.title": "Eliminar esta salida de herramienta",
|
"messageBlock.tool.deletePart.title": "Eliminar esta salida de herramienta",
|
||||||
"messageBlock.tool.deletePart.failed.title": "Error al eliminar",
|
"messageBlock.tool.deletePart.failed.title": "Error al eliminar",
|
||||||
@@ -77,11 +77,15 @@ export const messagingMessages = {
|
|||||||
"messageItem.actions.copy": "Copiar",
|
"messageItem.actions.copy": "Copiar",
|
||||||
"messageItem.actions.copyTitle": "Copiar mensaje",
|
"messageItem.actions.copyTitle": "Copiar mensaje",
|
||||||
"messageItem.actions.copied": "¡Copiado!",
|
"messageItem.actions.copied": "¡Copiado!",
|
||||||
|
"messageItem.actions.deleteMessage": "Eliminar mensaje",
|
||||||
|
"messageItem.actions.deletingMessage": "Eliminando...",
|
||||||
|
"messageItem.actions.deleteMessageFailedTitle": "Error al eliminar",
|
||||||
|
"messageItem.actions.deleteMessageFailedMessage": "No se pudo eliminar el mensaje",
|
||||||
"messageItem.status.queued": "EN COLA",
|
"messageItem.status.queued": "EN COLA",
|
||||||
"messageItem.status.generating": "Generando...",
|
"messageItem.status.generating": "Generando...",
|
||||||
"messageItem.status.sending": "Enviando...",
|
"messageItem.status.sending": "Enviando...",
|
||||||
"messageItem.status.failedToSend": "No se pudo enviar el mensaje",
|
"messageItem.status.failedToSend": "No se pudo enviar el mensaje",
|
||||||
"messagePart.actions.delete": "Eliminar",
|
"messagePart.actions.delete": "Eliminar parte",
|
||||||
"messagePart.actions.deleting": "Eliminando...",
|
"messagePart.actions.deleting": "Eliminando...",
|
||||||
"messagePart.actions.deleteTitle": "Eliminar este elemento",
|
"messagePart.actions.deleteTitle": "Eliminar este elemento",
|
||||||
"messagePart.actions.deleteFailedTitle": "Error al eliminar",
|
"messagePart.actions.deleteFailedTitle": "Error al eliminar",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const messagingMessages = {
|
|||||||
"messageBlock.tool.goToSession.label": "Aller à la session",
|
"messageBlock.tool.goToSession.label": "Aller à la session",
|
||||||
"messageBlock.tool.goToSession.title": "Aller à la session",
|
"messageBlock.tool.goToSession.title": "Aller à la session",
|
||||||
"messageBlock.tool.goToSession.unavailableTitle": "Session pas encore disponible",
|
"messageBlock.tool.goToSession.unavailableTitle": "Session pas encore disponible",
|
||||||
"messageBlock.tool.deletePart.label": "Supprimer",
|
"messageBlock.tool.deletePart.label": "Supprimer la partie",
|
||||||
"messageBlock.tool.deletePart.deleting": "Suppression...",
|
"messageBlock.tool.deletePart.deleting": "Suppression...",
|
||||||
"messageBlock.tool.deletePart.title": "Supprimer cette sortie d'outil",
|
"messageBlock.tool.deletePart.title": "Supprimer cette sortie d'outil",
|
||||||
"messageBlock.tool.deletePart.failed.title": "Échec de suppression",
|
"messageBlock.tool.deletePart.failed.title": "Échec de suppression",
|
||||||
@@ -77,11 +77,15 @@ export const messagingMessages = {
|
|||||||
"messageItem.actions.copy": "Copier",
|
"messageItem.actions.copy": "Copier",
|
||||||
"messageItem.actions.copyTitle": "Copier le message",
|
"messageItem.actions.copyTitle": "Copier le message",
|
||||||
"messageItem.actions.copied": "Copié !",
|
"messageItem.actions.copied": "Copié !",
|
||||||
|
"messageItem.actions.deleteMessage": "Supprimer le message",
|
||||||
|
"messageItem.actions.deletingMessage": "Suppression...",
|
||||||
|
"messageItem.actions.deleteMessageFailedTitle": "Échec de suppression",
|
||||||
|
"messageItem.actions.deleteMessageFailedMessage": "Impossible de supprimer le message",
|
||||||
"messageItem.status.queued": "EN FILE",
|
"messageItem.status.queued": "EN FILE",
|
||||||
"messageItem.status.generating": "Génération...",
|
"messageItem.status.generating": "Génération...",
|
||||||
"messageItem.status.sending": "Envoi...",
|
"messageItem.status.sending": "Envoi...",
|
||||||
"messageItem.status.failedToSend": "Échec de l'envoi du message",
|
"messageItem.status.failedToSend": "Échec de l'envoi du message",
|
||||||
"messagePart.actions.delete": "Supprimer",
|
"messagePart.actions.delete": "Supprimer la partie",
|
||||||
"messagePart.actions.deleting": "Suppression...",
|
"messagePart.actions.deleting": "Suppression...",
|
||||||
"messagePart.actions.deleteTitle": "Supprimer cet élément",
|
"messagePart.actions.deleteTitle": "Supprimer cet élément",
|
||||||
"messagePart.actions.deleteFailedTitle": "Échec de suppression",
|
"messagePart.actions.deleteFailedTitle": "Échec de suppression",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const messagingMessages = {
|
|||||||
"messageBlock.tool.goToSession.label": "セッションへ移動",
|
"messageBlock.tool.goToSession.label": "セッションへ移動",
|
||||||
"messageBlock.tool.goToSession.title": "セッションへ移動",
|
"messageBlock.tool.goToSession.title": "セッションへ移動",
|
||||||
"messageBlock.tool.goToSession.unavailableTitle": "セッションはまだ利用できません",
|
"messageBlock.tool.goToSession.unavailableTitle": "セッションはまだ利用できません",
|
||||||
"messageBlock.tool.deletePart.label": "削除",
|
"messageBlock.tool.deletePart.label": "パートを削除",
|
||||||
"messageBlock.tool.deletePart.deleting": "削除中...",
|
"messageBlock.tool.deletePart.deleting": "削除中...",
|
||||||
"messageBlock.tool.deletePart.title": "このツール出力を削除",
|
"messageBlock.tool.deletePart.title": "このツール出力を削除",
|
||||||
"messageBlock.tool.deletePart.failed.title": "削除に失敗しました",
|
"messageBlock.tool.deletePart.failed.title": "削除に失敗しました",
|
||||||
@@ -77,11 +77,15 @@ export const messagingMessages = {
|
|||||||
"messageItem.actions.copy": "コピー",
|
"messageItem.actions.copy": "コピー",
|
||||||
"messageItem.actions.copyTitle": "メッセージをコピー",
|
"messageItem.actions.copyTitle": "メッセージをコピー",
|
||||||
"messageItem.actions.copied": "コピーしました!",
|
"messageItem.actions.copied": "コピーしました!",
|
||||||
|
"messageItem.actions.deleteMessage": "メッセージを削除",
|
||||||
|
"messageItem.actions.deletingMessage": "削除中...",
|
||||||
|
"messageItem.actions.deleteMessageFailedTitle": "削除に失敗しました",
|
||||||
|
"messageItem.actions.deleteMessageFailedMessage": "メッセージの削除に失敗しました",
|
||||||
"messageItem.status.queued": "待機中",
|
"messageItem.status.queued": "待機中",
|
||||||
"messageItem.status.generating": "生成中...",
|
"messageItem.status.generating": "生成中...",
|
||||||
"messageItem.status.sending": "送信中...",
|
"messageItem.status.sending": "送信中...",
|
||||||
"messageItem.status.failedToSend": "メッセージの送信に失敗しました",
|
"messageItem.status.failedToSend": "メッセージの送信に失敗しました",
|
||||||
"messagePart.actions.delete": "削除",
|
"messagePart.actions.delete": "パートを削除",
|
||||||
"messagePart.actions.deleting": "削除中...",
|
"messagePart.actions.deleting": "削除中...",
|
||||||
"messagePart.actions.deleteTitle": "この項目を削除",
|
"messagePart.actions.deleteTitle": "この項目を削除",
|
||||||
"messagePart.actions.deleteFailedTitle": "削除に失敗しました",
|
"messagePart.actions.deleteFailedTitle": "削除に失敗しました",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const messagingMessages = {
|
|||||||
"messageBlock.tool.goToSession.label": "Перейти к сессии",
|
"messageBlock.tool.goToSession.label": "Перейти к сессии",
|
||||||
"messageBlock.tool.goToSession.title": "Перейти к сессии",
|
"messageBlock.tool.goToSession.title": "Перейти к сессии",
|
||||||
"messageBlock.tool.goToSession.unavailableTitle": "Сессия пока недоступна",
|
"messageBlock.tool.goToSession.unavailableTitle": "Сессия пока недоступна",
|
||||||
"messageBlock.tool.deletePart.label": "Удалить",
|
"messageBlock.tool.deletePart.label": "Удалить часть",
|
||||||
"messageBlock.tool.deletePart.deleting": "Удаление...",
|
"messageBlock.tool.deletePart.deleting": "Удаление...",
|
||||||
"messageBlock.tool.deletePart.title": "Удалить этот вывод инструмента",
|
"messageBlock.tool.deletePart.title": "Удалить этот вывод инструмента",
|
||||||
"messageBlock.tool.deletePart.failed.title": "Ошибка удаления",
|
"messageBlock.tool.deletePart.failed.title": "Ошибка удаления",
|
||||||
@@ -77,11 +77,15 @@ export const messagingMessages = {
|
|||||||
"messageItem.actions.copy": "Копировать",
|
"messageItem.actions.copy": "Копировать",
|
||||||
"messageItem.actions.copyTitle": "Копировать сообщение",
|
"messageItem.actions.copyTitle": "Копировать сообщение",
|
||||||
"messageItem.actions.copied": "Скопировано!",
|
"messageItem.actions.copied": "Скопировано!",
|
||||||
|
"messageItem.actions.deleteMessage": "Удалить сообщение",
|
||||||
|
"messageItem.actions.deletingMessage": "Удаление...",
|
||||||
|
"messageItem.actions.deleteMessageFailedTitle": "Ошибка удаления",
|
||||||
|
"messageItem.actions.deleteMessageFailedMessage": "Не удалось удалить сообщение",
|
||||||
"messageItem.status.queued": "В ОЧЕРЕДИ",
|
"messageItem.status.queued": "В ОЧЕРЕДИ",
|
||||||
"messageItem.status.generating": "Генерация…",
|
"messageItem.status.generating": "Генерация…",
|
||||||
"messageItem.status.sending": "Отправка…",
|
"messageItem.status.sending": "Отправка…",
|
||||||
"messageItem.status.failedToSend": "Не удалось отправить сообщение",
|
"messageItem.status.failedToSend": "Не удалось отправить сообщение",
|
||||||
"messagePart.actions.delete": "Удалить",
|
"messagePart.actions.delete": "Удалить часть",
|
||||||
"messagePart.actions.deleting": "Удаление...",
|
"messagePart.actions.deleting": "Удаление...",
|
||||||
"messagePart.actions.deleteTitle": "Удалить этот элемент",
|
"messagePart.actions.deleteTitle": "Удалить этот элемент",
|
||||||
"messagePart.actions.deleteFailedTitle": "Ошибка удаления",
|
"messagePart.actions.deleteFailedTitle": "Ошибка удаления",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const messagingMessages = {
|
|||||||
"messageBlock.tool.goToSession.label": "前往会话",
|
"messageBlock.tool.goToSession.label": "前往会话",
|
||||||
"messageBlock.tool.goToSession.title": "前往会话",
|
"messageBlock.tool.goToSession.title": "前往会话",
|
||||||
"messageBlock.tool.goToSession.unavailableTitle": "会话尚不可用",
|
"messageBlock.tool.goToSession.unavailableTitle": "会话尚不可用",
|
||||||
"messageBlock.tool.deletePart.label": "删除",
|
"messageBlock.tool.deletePart.label": "删除部分",
|
||||||
"messageBlock.tool.deletePart.deleting": "正在删除...",
|
"messageBlock.tool.deletePart.deleting": "正在删除...",
|
||||||
"messageBlock.tool.deletePart.title": "删除此工具输出",
|
"messageBlock.tool.deletePart.title": "删除此工具输出",
|
||||||
"messageBlock.tool.deletePart.failed.title": "删除失败",
|
"messageBlock.tool.deletePart.failed.title": "删除失败",
|
||||||
@@ -77,11 +77,15 @@ export const messagingMessages = {
|
|||||||
"messageItem.actions.copy": "复制",
|
"messageItem.actions.copy": "复制",
|
||||||
"messageItem.actions.copyTitle": "复制消息",
|
"messageItem.actions.copyTitle": "复制消息",
|
||||||
"messageItem.actions.copied": "已复制!",
|
"messageItem.actions.copied": "已复制!",
|
||||||
|
"messageItem.actions.deleteMessage": "删除消息",
|
||||||
|
"messageItem.actions.deletingMessage": "正在删除...",
|
||||||
|
"messageItem.actions.deleteMessageFailedTitle": "删除失败",
|
||||||
|
"messageItem.actions.deleteMessageFailedMessage": "无法删除消息",
|
||||||
"messageItem.status.queued": "排队中",
|
"messageItem.status.queued": "排队中",
|
||||||
"messageItem.status.generating": "正在生成...",
|
"messageItem.status.generating": "正在生成...",
|
||||||
"messageItem.status.sending": "正在发送...",
|
"messageItem.status.sending": "正在发送...",
|
||||||
"messageItem.status.failedToSend": "消息发送失败",
|
"messageItem.status.failedToSend": "消息发送失败",
|
||||||
"messagePart.actions.delete": "删除",
|
"messagePart.actions.delete": "删除部分",
|
||||||
"messagePart.actions.deleting": "正在删除...",
|
"messagePart.actions.deleting": "正在删除...",
|
||||||
"messagePart.actions.deleteTitle": "删除此项",
|
"messagePart.actions.deleteTitle": "删除此项",
|
||||||
"messagePart.actions.deleteFailedTitle": "删除失败",
|
"messagePart.actions.deleteFailedTitle": "删除失败",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { providers, sessions, withSession } from "./session-state"
|
|||||||
import { getDefaultModel, isModelValid } from "./session-models"
|
import { getDefaultModel, isModelValid } from "./session-models"
|
||||||
import { updateSessionInfo } from "./message-v2/session-info"
|
import { updateSessionInfo } from "./message-v2/session-info"
|
||||||
import { messageStoreBus } from "./message-v2/bus"
|
import { messageStoreBus } from "./message-v2/bus"
|
||||||
import { removeMessagePartV2 } from "./message-v2/bridge"
|
import { removeMessagePartV2, removeMessageV2 } from "./message-v2/bridge"
|
||||||
import { getLogger } from "../lib/logger"
|
import { getLogger } from "../lib/logger"
|
||||||
import { requestData } from "../lib/opencode-api"
|
import { requestData } from "../lib/opencode-api"
|
||||||
|
|
||||||
@@ -439,8 +439,33 @@ async function deleteMessagePart(instanceId: string, sessionId: string, messageI
|
|||||||
updateSessionInfo(instanceId, sessionId)
|
updateSessionInfo(instanceId, sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteMessage(instanceId: string, sessionId: string, messageId: string): Promise<void> {
|
||||||
|
if (!instanceId || !sessionId || !messageId) return
|
||||||
|
const instance = instances().get(instanceId)
|
||||||
|
if (!instance || !instance.client) {
|
||||||
|
throw new Error("Instance not ready")
|
||||||
|
}
|
||||||
|
|
||||||
|
const worktreeSlug = getWorktreeSlugForSession(instanceId, sessionId)
|
||||||
|
const client = getOrCreateWorktreeClient(instanceId, worktreeSlug)
|
||||||
|
|
||||||
|
// The SDK generator does not currently expose a typed method for deleting a message,
|
||||||
|
// but the API is available at DELETE /session/:sessionID/message/:messageID.
|
||||||
|
await requestData(
|
||||||
|
(client as any).client.delete({
|
||||||
|
url: `/session/${encodeURIComponent(sessionId)}/message/${encodeURIComponent(messageId)}`,
|
||||||
|
}),
|
||||||
|
"session.message.delete",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Optimistic removal; SSE will also broadcast a message-removed event.
|
||||||
|
removeMessageV2(instanceId, messageId)
|
||||||
|
updateSessionInfo(instanceId, sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
abortSession,
|
abortSession,
|
||||||
|
deleteMessage,
|
||||||
deleteMessagePart,
|
deleteMessagePart,
|
||||||
executeCustomCommand,
|
executeCustomCommand,
|
||||||
renameSession,
|
renameSession,
|
||||||
|
|||||||
Reference in New Issue
Block a user