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 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>
); );

View file

@ -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);

View file

@ -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}
/> />
); );
})} })}

View file

@ -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>;
} }

View file

@ -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"
/> />
); );