fixed 3 issues
This commit is contained in:
parent
def3ba06de
commit
a83aa31df7
5 changed files with 179 additions and 11 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
import React from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
import { FaExclamationTriangle } from 'react-icons/fa';
|
import { FaExclamationTriangle } from 'react-icons/fa';
|
||||||
|
import { IoIosTrash, IoIosCheckmark, IoIosClose } from 'react-icons/io';
|
||||||
import { Message } from '../MessagesTypes';
|
import { Message } from '../MessagesTypes';
|
||||||
import { formatTimestamp } from '../MessageUtils';
|
import { formatTimestamp } from '../MessageUtils';
|
||||||
import { DocumentItem, ActionInfo } from '../MessageParts';
|
import { DocumentItem, ActionInfo } from '../MessageParts';
|
||||||
|
|
@ -21,6 +22,8 @@ export interface ChatMessageProps {
|
||||||
removingFiles?: Set<string>;
|
removingFiles?: Set<string>;
|
||||||
downloadingFiles?: Set<string>;
|
downloadingFiles?: Set<string>;
|
||||||
workflowId?: string;
|
workflowId?: string;
|
||||||
|
onMessageDelete?: (messageId: string) => Promise<void>;
|
||||||
|
deletingMessages?: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,12 +41,36 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
||||||
previewingFiles,
|
previewingFiles,
|
||||||
removingFiles,
|
removingFiles,
|
||||||
downloadingFiles,
|
downloadingFiles,
|
||||||
workflowId
|
workflowId,
|
||||||
|
onMessageDelete,
|
||||||
|
deletingMessages
|
||||||
}) => {
|
}) => {
|
||||||
const isUser = message.role?.toLowerCase() === 'user';
|
const isUser = message.role?.toLowerCase() === 'user';
|
||||||
const isError = message.actionProgress === 'fail' || message.actionProgress === 'error';
|
const isError = message.actionProgress === 'fail' || message.actionProgress === 'error';
|
||||||
const messageClass = isUser ? styles.messageUser : styles.messageAssistant;
|
const messageClass = isUser ? styles.messageUser : styles.messageAssistant;
|
||||||
const errorClass = isError ? styles.messageError : '';
|
const errorClass = isError ? styles.messageError : '';
|
||||||
|
const isDeleting = deletingMessages?.has(message.id) || false;
|
||||||
|
|
||||||
|
// Message delete 2-click confirmation state
|
||||||
|
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);
|
||||||
|
|
||||||
|
const handleDeleteClick = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsConfirmingDelete(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmDelete = async (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsConfirmingDelete(false);
|
||||||
|
if (onMessageDelete && message.id) {
|
||||||
|
await onMessageDelete(message.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelDelete = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsConfirmingDelete(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.message} ${messageClass} ${errorClass}`}>
|
<div className={`${styles.message} ${messageClass} ${errorClass}`}>
|
||||||
|
|
@ -158,12 +185,47 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
||||||
{/* Action information (shown only for assistant messages) */}
|
{/* Action information (shown only for assistant messages) */}
|
||||||
{!isUser && <ActionInfo message={message} />}
|
{!isUser && <ActionInfo message={message} />}
|
||||||
|
|
||||||
{/* Timestamp */}
|
{/* Timestamp and message actions */}
|
||||||
|
<div className={styles.messageFooter}>
|
||||||
{message.publishedAt && (
|
{message.publishedAt && (
|
||||||
<div className={styles.messageTimestamp}>
|
<div className={styles.messageTimestamp}>
|
||||||
{formatTimestamp(message.publishedAt)}
|
{formatTimestamp(message.publishedAt)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{onMessageDelete && message.id && (
|
||||||
|
<div className={styles.messageActions}>
|
||||||
|
{isConfirmingDelete ? (
|
||||||
|
<div className={styles.messageDeleteConfirm}>
|
||||||
|
<button
|
||||||
|
onClick={handleConfirmDelete}
|
||||||
|
className={styles.messageDeleteConfirmBtn}
|
||||||
|
title="Löschen bestätigen"
|
||||||
|
disabled={isDeleting}
|
||||||
|
>
|
||||||
|
<IoIosCheckmark />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleCancelDelete}
|
||||||
|
className={styles.messageDeleteCancelBtn}
|
||||||
|
title="Abbrechen"
|
||||||
|
disabled={isDeleting}
|
||||||
|
>
|
||||||
|
<IoIosClose />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={handleDeleteClick}
|
||||||
|
className={styles.messageDeleteBtn}
|
||||||
|
title="Nachricht löschen"
|
||||||
|
disabled={isDeleting}
|
||||||
|
>
|
||||||
|
<IoIosTrash />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -512,11 +512,17 @@
|
||||||
border-top-color: var(--color-gray-disabled);
|
border-top-color: var(--color-gray-disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.messageFooter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 4px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.messageTimestamp {
|
.messageTimestamp {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
margin-top: 4px;
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageUser .messageTimestamp {
|
.messageUser .messageTimestamp {
|
||||||
|
|
@ -527,6 +533,92 @@
|
||||||
color: var(--color-gray);
|
color: var(--color-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Message action buttons (delete) */
|
||||||
|
.messageActions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message:hover .messageActions {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteBtn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-gray);
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteBtn:hover {
|
||||||
|
color: var(--color-danger, #dc3545);
|
||||||
|
background: rgba(220, 53, 69, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteBtn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageUser .messageDeleteBtn {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageUser .messageDeleteBtn:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message delete confirmation */
|
||||||
|
.messageDeleteConfirm {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
background: var(--color-secondary);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteConfirmBtn,
|
||||||
|
.messageDeleteCancelBtn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 16px;
|
||||||
|
color: white;
|
||||||
|
min-width: 22px;
|
||||||
|
min-height: 22px;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteConfirmBtn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteCancelBtn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageDeleteConfirmBtn:disabled,
|
||||||
|
.messageDeleteCancelBtn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.messageSummary {
|
.messageSummary {
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: var(--object-radius-small);
|
border-radius: var(--object-radius-small);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ const Messages: React.FC<MessagesProps> = ({
|
||||||
previewingFiles,
|
previewingFiles,
|
||||||
removingFiles,
|
removingFiles,
|
||||||
downloadingFiles,
|
downloadingFiles,
|
||||||
workflowId
|
workflowId,
|
||||||
|
onMessageDelete,
|
||||||
|
deletingMessages
|
||||||
}) => {
|
}) => {
|
||||||
if (!messages || messages.length === 0) {
|
if (!messages || messages.length === 0) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -89,6 +91,8 @@ const Messages: React.FC<MessagesProps> = ({
|
||||||
removingFiles={removingFiles}
|
removingFiles={removingFiles}
|
||||||
downloadingFiles={downloadingFiles}
|
downloadingFiles={downloadingFiles}
|
||||||
workflowId={workflowId}
|
workflowId={workflowId}
|
||||||
|
onMessageDelete={onMessageDelete}
|
||||||
|
deletingMessages={deletingMessages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -116,5 +116,11 @@ export interface MessagesProps {
|
||||||
removingFiles?: Set<string>;
|
removingFiles?: Set<string>;
|
||||||
downloadingFiles?: Set<string>;
|
downloadingFiles?: Set<string>;
|
||||||
workflowId?: string;
|
workflowId?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message delete handler
|
||||||
|
*/
|
||||||
|
onMessageDelete?: (messageId: string) => Promise<void>;
|
||||||
|
deletingMessages?: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ export const PlaygroundPage: React.FC = () => {
|
||||||
deletingFiles,
|
deletingFiles,
|
||||||
previewingFiles,
|
previewingFiles,
|
||||||
downloadingFiles,
|
downloadingFiles,
|
||||||
|
handleMessageDelete,
|
||||||
|
deletingMessages,
|
||||||
selectedProviders,
|
selectedProviders,
|
||||||
onProvidersChange,
|
onProvidersChange,
|
||||||
} = hookData;
|
} = hookData;
|
||||||
|
|
@ -349,6 +351,8 @@ export const PlaygroundPage: React.FC = () => {
|
||||||
previewingFiles={previewingFiles}
|
previewingFiles={previewingFiles}
|
||||||
downloadingFiles={downloadingFiles}
|
downloadingFiles={downloadingFiles}
|
||||||
workflowId={workflowId}
|
workflowId={workflowId}
|
||||||
|
onMessageDelete={handleMessageDelete}
|
||||||
|
deletingMessages={deletingMessages}
|
||||||
emptyMessage="Keine Nachrichten"
|
emptyMessage="Keine Nachrichten"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue