Commit c3f99f53 authored by edy's avatar edy

feat(ui): add chat message delete action

parent 286f1972
......@@ -31,6 +31,7 @@ import {
RedBookIcon,
RefreshIcon,
ThumbIcon,
TrashIcon,
getIntentSuggestionIcon,
renderExpertIcon
} from "./components/icons/AppIcons";
......@@ -404,7 +405,8 @@ export default function App() {
renameMessageTrace,
initializeMessageTrace,
setMessageTraceExpanded,
collapseMessageTrace
collapseMessageTrace,
removeMessageTrace
} = useMessageTraces();
const {
saveConfig,
......@@ -1024,6 +1026,28 @@ export default function App() {
}
}
function deleteMessage(messageId: string) {
if (!visibleSessionId) {
return;
}
updateSessionMessages(visibleSessionId, (current) => {
const nextMessages = current.filter((message) => message.id !== messageId);
return nextMessages.length === current.length ? current : nextMessages;
});
removeMessageTrace(messageId);
setMessageReactions((current) => {
if (!(messageId in current)) {
return current;
}
const { [messageId]: _removed, ...rest } = current;
return rest;
});
if (copiedToken === `message:${messageId}`) {
setCopiedToken("");
}
}
async function regenerateAssistantMessage(messageId: string) {
if (sending || !visibleSessionId || !sessionScopeProjectId) {
return;
......@@ -1219,6 +1243,7 @@ export default function App() {
},
copyIcon: <CopyIcon />,
copiedIcon: <CheckIcon />,
deleteIcon: <TrashIcon />,
regenerateIcon: <RefreshIcon />,
renderThumbIcon: (direction) => <ThumbIcon direction={direction} />,
renderMarkdownContent,
......@@ -1226,6 +1251,7 @@ export default function App() {
formatMessageTimestamp,
onMessageListScroll: handleMessageListScroll,
onCopyText: handleCopyText,
onDeleteMessage: deleteMessage,
onTraceExpandedChange: setMessageTraceExpanded,
onRegenerateAssistantMessage: regenerateAssistantMessage,
onToggleMessageReaction: toggleMessageReaction,
......
......@@ -309,6 +309,17 @@ export function CheckIcon() {
);
}
export function TrashIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
<path d="M5 7h14" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
<path d="M10 11v6M14 11v6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" />
<path d="M8 7l.6 12.2A2 2 0 0 0 10.6 21h2.8a2 2 0 0 0 2-1.8L16 7" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9 7V5.6A1.6 1.6 0 0 1 10.6 4h2.8A1.6 1.6 0 0 1 15 5.6V7" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
export function ArrowUpIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
......
......@@ -87,6 +87,7 @@ interface ConversationWorkspaceViewProps {
}
copyIcon: ReactNode
copiedIcon: ReactNode
deleteIcon: ReactNode
regenerateIcon: ReactNode
renderThumbIcon: (direction: MessageReaction) => ReactNode
renderMarkdownContent: (
......@@ -105,6 +106,7 @@ interface ConversationWorkspaceViewProps {
formatMessageTimestamp: (value: string) => string
onMessageListScroll: (event: ReactUIEvent<HTMLDivElement>) => void
onCopyText: (token: string, text: string) => void | Promise<void>
onDeleteMessage: (messageId: string) => void
onTraceExpandedChange: (messageId: string, expanded: boolean) => void
onRegenerateAssistantMessage: (messageId: string) => void | Promise<void>
onToggleMessageReaction: (messageId: string, reaction: MessageReaction) => void
......@@ -187,6 +189,7 @@ export function ConversationWorkspaceView({
messageLabels,
copyIcon,
copiedIcon,
deleteIcon,
regenerateIcon,
renderThumbIcon,
renderMarkdownContent,
......@@ -194,6 +197,7 @@ export function ConversationWorkspaceView({
formatMessageTimestamp,
onMessageListScroll,
onCopyText,
onDeleteMessage,
onTraceExpandedChange,
onRegenerateAssistantMessage,
onToggleMessageReaction,
......@@ -342,6 +346,7 @@ export function ConversationWorkspaceView({
labels={messageLabels}
copyIcon={copyIcon}
copiedIcon={copiedIcon}
deleteIcon={deleteIcon}
regenerateIcon={regenerateIcon}
renderThumbIcon={renderThumbIcon}
renderMarkdownContent={renderMarkdownContent}
......@@ -349,6 +354,7 @@ export function ConversationWorkspaceView({
formatMessageTimestamp={formatMessageTimestamp}
onScroll={onMessageListScroll}
onCopyText={onCopyText}
onDeleteMessage={onDeleteMessage}
onTraceExpandedChange={onTraceExpandedChange}
onRegenerateAssistantMessage={onRegenerateAssistantMessage}
onToggleMessageReaction={onToggleMessageReaction}
......
This diff is collapsed.
......@@ -96,12 +96,23 @@ export function useMessageTraces() {
setMessageTraceExpanded(messageId, false)
}, [setMessageTraceExpanded])
const removeMessageTrace = useCallback((messageId: string) => {
setMessageTraces((current) => {
if (!(messageId in current)) {
return current
}
const { [messageId]: _removed, ...rest } = current
return rest
})
}, [])
return {
messageTraces,
renameMessageTrace,
initializeMessageTrace,
appendTrace,
setMessageTraceExpanded,
collapseMessageTrace
collapseMessageTrace,
removeMessageTrace
}
}
......@@ -747,10 +747,17 @@
}
.conversation-shell .message-bubble,
.conversation-shell .message-card-body,
.conversation-shell .message-bubble-assistant,
.conversation-shell .message-card.assistant .message-bubble {
max-width: 100%;
}
.conversation-shell .message-card-meta {
opacity: 0.76;
pointer-events: auto;
transform: translateY(0);
}
}
@media (max-width: 720px) {
......
......@@ -270,6 +270,33 @@
margin-top: 6px;
}
.message-card-body {
display: grid;
gap: 4px;
max-width: 100%;
}
.message-card-body-user {
justify-self: end;
}
.message-card-body-assistant {
justify-self: start;
}
.message-card-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
min-height: 28px;
padding: 0 2px;
opacity: 0;
pointer-events: none;
transform: translateY(-2px);
transition: opacity 150ms ease, transform 150ms ease;
}
.thinking-spinner {
width: 16px;
height: 16px;
......@@ -819,30 +846,57 @@
.message-card-actions {
display: flex;
align-items: center;
gap: 8px;
margin-top: 12px;
justify-content: flex-start;
opacity: 0;
transition: opacity 150ms ease;
gap: 1px;
width: max-content;
margin-top: 0;
padding: 2px;
border: 1px solid rgba(203, 213, 225, 0.68);
border-radius: 999px;
background: rgba(248, 250, 252, 0.9);
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
backdrop-filter: blur(12px);
justify-content: flex-end;
pointer-events: auto;
opacity: 1;
}
.message-card:hover .message-card-actions {
.message-card:hover .message-card-meta,
.message-card:focus-within .message-card-meta {
pointer-events: auto;
opacity: 1;
transform: translateY(0);
}
.message-action-delete:hover {
background: #fef2f2;
color: #dc2626;
}
.message-action-icon {
width: 32px;
height: 32px;
width: 26px;
height: 26px;
padding: 0;
border: 0;
border-radius: 999px;
background: #ffffff;
color: #5f7773;
box-shadow: 0 10px 24px rgba(17, 24, 39, 0.08);
background: transparent;
color: #738196;
cursor: pointer;
box-shadow: none;
transition: background-color 150ms ease, color 150ms ease, transform 120ms ease;
}
.message-action-icon:hover {
background: #f4fbfa;
background: #eef6ff;
color: #0f67de;
}
.message-action-icon:active {
transform: scale(0.94);
}
.message-action-icon:focus-visible {
outline: 2px solid rgba(15, 103, 222, 0.28);
outline-offset: 2px;
}
.message-action-icon.copied {
......
......@@ -1014,6 +1014,23 @@
justify-content: flex-start;
}
.conversation-shell .message-card-body {
width: auto;
max-width: min(72%, 760px);
}
.conversation-shell .message-card-body-user {
margin-left: auto;
}
.conversation-shell .message-card-body-assistant {
max-width: min(100%, 880px);
}
.conversation-shell .message-card-body .message-bubble {
max-width: 100%;
}
.conversation-shell .message-bubble {
width: auto;
display: grid;
......@@ -1095,30 +1112,17 @@
}
.conversation-shell .message-timestamp {
position: absolute;
bottom: 0;
left: 4px;
z-index: 1;
position: static;
color: #7c8da3;
font-size: 11px;
line-height: 1;
opacity: 0;
pointer-events: none;
transition: opacity 140ms ease;
}
.conversation-shell .message-card.user .message-timestamp {
left: auto;
right: 4px;
}
.conversation-shell .message-card:hover .message-timestamp,
.conversation-shell .message-card:focus-within .message-timestamp {
opacity: 1;
pointer-events: none;
white-space: nowrap;
}
.conversation-shell .message-card-actions {
margin-top: 8px;
margin-right: 0;
}
.conversation-shell .composer-shell {
......
......@@ -73,6 +73,7 @@
.nav-item-icon svg,
.expert-chip-icon svg,
.message-action-icon svg,
.message-action-delete svg,
.composer-submit svg,
.attachment-trigger svg,
.markdown-code-copy svg {
......@@ -104,11 +105,13 @@
@apply mb-0;
}
.message-card.assistant .message-card-actions {
.message-card .message-card-meta {
@apply pointer-events-none;
}
.message-card.assistant:hover .message-card-actions {
.message-card:hover .message-card-meta,
.message-card:focus-within .message-card-meta,
.message-card .message-card-meta:focus-within {
@apply pointer-events-auto;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment