fixed 3 issues

This commit is contained in:
ValueOn AG 2026-02-12 00:42:09 +01:00
parent def3ba06de
commit a83aa31df7
5 changed files with 179 additions and 11 deletions

View file

@ -1,7 +1,8 @@
import React from 'react';
import React, { useState, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { FaExclamationTriangle } from 'react-icons/fa';
import { IoIosTrash, IoIosCheckmark, IoIosClose } from 'react-icons/io';
import { Message } from '../MessagesTypes';
import { formatTimestamp } from '../MessageUtils';
import { DocumentItem, ActionInfo } from '../MessageParts';
@ -21,6 +22,8 @@ export interface ChatMessageProps {
removingFiles?: Set<string>;
downloadingFiles?: Set<string>;
workflowId?: string;
onMessageDelete?: (messageId: string) => Promise<void>;
deletingMessages?: Set<string>;
}
/**
@ -38,12 +41,36 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
previewingFiles,
removingFiles,
downloadingFiles,
workflowId
workflowId,
onMessageDelete,
deletingMessages
}) => {
const isUser = message.role?.toLowerCase() === 'user';
const isError = message.actionProgress === 'fail' || message.actionProgress === 'error';
const messageClass = isUser ? styles.messageUser : styles.messageAssistant;
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 (
<div className={`${styles.message} ${messageClass} ${errorClass}`}>
@ -158,12 +185,47 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
{/* Action information (shown only for assistant messages) */}
{!isUser && <ActionInfo message={message} />}
{/* Timestamp */}
{message.publishedAt && (
<div className={styles.messageTimestamp}>
{formatTimestamp(message.publishedAt)}
</div>
)}
{/* Timestamp and message actions */}
<div className={styles.messageFooter}>
{message.publishedAt && (
<div className={styles.messageTimestamp}>
{formatTimestamp(message.publishedAt)}
</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>
);

View file

@ -512,11 +512,17 @@
border-top-color: var(--color-gray-disabled);
}
.messageFooter {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 4px;
gap: 8px;
}
.messageTimestamp {
font-size: 11px;
opacity: 0.7;
margin-top: 4px;
align-self: flex-end;
}
.messageUser .messageTimestamp {
@ -527,6 +533,92 @@
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 {
padding: 8px 12px;
border-radius: var(--object-radius-small);

View file

@ -24,7 +24,9 @@ const Messages: React.FC<MessagesProps> = ({
previewingFiles,
removingFiles,
downloadingFiles,
workflowId
workflowId,
onMessageDelete,
deletingMessages
}) => {
if (!messages || messages.length === 0) {
return (
@ -89,6 +91,8 @@ const Messages: React.FC<MessagesProps> = ({
removingFiles={removingFiles}
downloadingFiles={downloadingFiles}
workflowId={workflowId}
onMessageDelete={onMessageDelete}
deletingMessages={deletingMessages}
/>
);
})}

View file

@ -116,5 +116,11 @@ export interface MessagesProps {
removingFiles?: Set<string>;
downloadingFiles?: Set<string>;
workflowId?: string;
/**
* Message delete handler
*/
onMessageDelete?: (messageId: string) => Promise<void>;
deletingMessages?: Set<string>;
}

View file

@ -58,6 +58,8 @@ export const PlaygroundPage: React.FC = () => {
deletingFiles,
previewingFiles,
downloadingFiles,
handleMessageDelete,
deletingMessages,
selectedProviders,
onProvidersChange,
} = hookData;
@ -349,6 +351,8 @@ export const PlaygroundPage: React.FC = () => {
previewingFiles={previewingFiles}
downloadingFiles={downloadingFiles}
workflowId={workflowId}
onMessageDelete={handleMessageDelete}
deletingMessages={deletingMessages}
emptyMessage="Keine Nachrichten"
/>
);