diff --git a/src/components/chat/ChatFlowList.jsx b/src/components/chat/ChatFlowList.jsx
index 11571be..bf1a2a6 100644
--- a/src/components/chat/ChatFlowList.jsx
+++ b/src/components/chat/ChatFlowList.jsx
@@ -8,11 +8,10 @@ import PsychologyRoundedIcon from '@mui/icons-material/PsychologyRounded';
import BuildRoundedIcon from '@mui/icons-material/BuildRounded';
import HealingMarkdown from './HealingMarkdown';
import {
+ CHROMA,
SHADOW_BUBBLE_ASSISTANT,
SHADOW_BUBBLE_USER,
SURFACE_RAISED,
- SURFACE_SUBTLE_PAIR,
- byMode,
} from '../../colors';
function formatMessageTime(isoString) {
@@ -47,44 +46,42 @@ function formatUsageText(usage) {
}
function formatToolTooltip(content, result, parameters) {
- let text = '';
+ const parts = [];
+
if (parameters !== undefined && parameters !== null && parameters !== '') {
try {
const parsed = typeof parameters === 'string' ? JSON.parse(parameters) : parameters;
- text = 'Aufruf:\n' + (typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : String(parameters));
+ parts.push(typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : String(parameters));
} catch {
- text = 'Aufruf:\n' + String(parameters);
+ parts.push(String(parameters));
}
}
- let contentText = '';
if (content !== undefined && content !== null && content !== '') {
try {
const parsed = typeof content === 'string' ? JSON.parse(content) : content;
- contentText = typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : String(content);
+ const str = typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : String(content);
+ if (!parts.includes(str)) {
+ parts.push(str);
+ }
} catch {
- contentText = String(content);
- }
- }
-
- if (contentText) {
- if (text) {
- text += '\n\nErgebnis:\n' + contentText;
- } else {
- text = contentText;
+ const str = String(content);
+ if (!parts.includes(str)) {
+ parts.push(str);
+ }
}
}
if (result !== undefined && result !== null) {
try {
const resStr = typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result);
- if (!text.includes(resStr)) {
- text = text ? text + '\n\n' + resStr : resStr;
+ if (!parts.some((p) => p.includes(resStr))) {
+ parts.push(resStr);
}
} catch {}
}
- return text || 'Werkzeug wird ausgeführt…';
+ return parts.join('\n\n') || '';
}
class ClickTooltip extends PureComponent {
@@ -110,6 +107,10 @@ class ClickTooltip extends PureComponent {
disableFocusListener
disableHoverListener
disableTouchListener
+ enterDelay={0}
+ leaveDelay={0}
+ TransitionProps={{ timeout: 0 }}
+ slotProps={{ transition: { timeout: 0 } }}
title={title}
arrow
placement="top"
@@ -138,23 +139,19 @@ function HandshakeIconView({ state }) {
return (
({
+ sx={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 24,
height: 24,
color: isGreen
- ? theme.palette.mode === 'dark'
- ? '#66bb6a'
- : '#2e7d32'
+ ? CHROMA.GREEN.dark
: isBlack
- ? theme.palette.mode === 'dark'
- ? '#f0f0f0'
- : '#111111'
+ ? '#000000'
: '#9e9e9e',
transition: 'color 180ms ease-in-out',
- })}
+ }}
>
@@ -163,7 +160,7 @@ function HandshakeIconView({ state }) {
function BrainIconView({ reasoning }) {
const tooltipContent = reasoning?.text?.trim() || '';
- if (!tooltipContent) return null;
+ if (!tooltipContent && reasoning?.sealed) return null;
return (
- {tooltipContent}
+ {tooltipContent || 'Wird überlegt…'}
}
>
({
+ sx={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 24,
height: 24,
- color: theme.palette.mode === 'dark' ? '#b388ff' : '#651fff',
+ color: CHROMA.PURPLE.dark,
borderRadius: 1,
'&:hover': {
bgcolor: 'action.hover',
},
- })}
+ }}
>
@@ -227,24 +224,18 @@ function WorkIconView({ tool }) {
}
>
({
+ sx={{
display: 'inline-flex',
alignItems: 'center',
height: 24,
gap: 0.5,
- color: isGreen
- ? theme.palette.mode === 'dark'
- ? '#66bb6a'
- : '#2e7d32'
- : theme.palette.mode === 'dark'
- ? '#f0f0f0'
- : '#111111',
+ color: isGreen ? CHROMA.GREEN.dark : '#000000',
px: 0.25,
borderRadius: 1,
'&:hover': {
bgcolor: 'action.hover',
},
- })}
+ }}
>
{tool?.name ? (
@@ -294,7 +285,14 @@ function UsageChipView({ usage }) {
export function TurnRow({ turn, isLive = false }) {
if (!turn) return null;
+ const hasReasonings = Boolean(
+ turn.reasonings &&
+ turn.reasonings.some((r) => !r.sealed || Boolean(r.text && r.text.trim()))
+ );
+ const hasTools = Boolean(turn.tools && turn.tools.length > 0);
const hasContent = Boolean(turn.content);
+ const showHandshake = !hasReasonings && !hasTools && !hasContent;
+ const showStatusLine = showHandshake || hasReasonings || hasTools;
return (
{/* Turn status line: left of the chat */}
-
-
+ {showStatusLine ? (
+
+ {showHandshake ? : null}
- {turn.reasonings?.map((r, i) => (
-
- ))}
+ {turn.reasonings?.map((r, i) => (
+
+ ))}
- {turn.tools?.map((t, i) => (
-
- ))}
-
+ {turn.tools?.map((t, i) => (
+
+ ))}
+
+ ) : null}
{/* Assistant message content for this turn (if any) */}
{hasContent ? (
@@ -543,15 +543,8 @@ function MessageRow({ msg, showTime = true }) {
const payload = parseMessagePayload(msg.message);
const textContent = payload.content !== undefined ? payload.content : msg.message;
- const toolCalls = Array.isArray(payload.tool_calls) && payload.tool_calls.length > 0 ? payload.tool_calls : null;
- const isUser = msg.role === 'user';
- const isSystem = msg.role === 'system';
- const isTool = msg.role === 'tool';
- const isReasoning = msg.role === 'reasoning' || Boolean(payload.reasoning_content && !toolCalls && payload.content === payload.reasoning_content);
- const timeStr = showTime ? formatMessageTime(msg.created_at) : '';
-
- if (isSystem) {
+ if (msg.role === 'system') {
return (
-
-
- );
- }
-
- if (toolCalls) {
- const formattedCalls = toolCalls
- .map((tc) => {
- let args = {};
- try {
- if (typeof tc.function?.arguments === 'string') {
- args = JSON.parse(tc.function.arguments);
- } else if (tc.function?.arguments) {
- args = tc.function.arguments;
- }
- } catch {}
-
- if (tc.function?.name === 'ask_user') {
- const q = args.question || '';
- return `ask_user: ${q}`;
- }
-
- const method = args.method || (tc.function?.name ? tc.function.name.toUpperCase() : 'CALL');
- const path = args.path || args.url || '';
- if (path) {
- return `${method} ${path}`;
- }
- return `${tc.function?.name || 'tool'} ${JSON.stringify(args)}`;
- })
- .join('\n');
-
- return (
-
- ({
- bgcolor: byMode(theme, SURFACE_SUBTLE_PAIR),
- color: 'text.secondary',
- px: 1.25,
- py: 0.35,
- borderRadius: 2,
- fontSize: '0.72rem',
- maxWidth: '90%',
- fontFamily: 'monospace',
- border: `1px solid ${theme.palette.divider}`,
- display: 'inline-flex',
- alignItems: 'center',
- gap: 1,
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- })}
- >
-
- Werkzeug
-
-
- {formattedCalls}
-
-
-
- );
- }
-
- if (isTool) {
- let statusText = '';
- let bodyLength = null;
-
- let resObj = null;
- if (typeof textContent === 'object' && textContent !== null) {
- resObj = textContent;
- } else if (typeof textContent === 'string') {
- try {
- const parsed = JSON.parse(textContent);
- if (parsed && typeof parsed === 'object') {
- resObj = parsed;
- }
- } catch {}
- }
-
- if (resObj && ('status' in resObj || 'statusCode' in resObj || 'body' in resObj)) {
- const status = resObj.status ?? resObj.statusCode ?? 200;
- statusText = `Status ${status}`;
- if (typeof resObj.body === 'string') {
- bodyLength = resObj.body.length;
- } else if (resObj.body != null) {
- bodyLength = JSON.stringify(resObj.body).length;
- } else if (resObj.data != null) {
- bodyLength = JSON.stringify(resObj.data).length;
- }
- } else if (typeof textContent === 'string') {
- bodyLength = textContent.length;
- }
-
- const isToolAnswer =
- Boolean(resObj) &&
- ('answer' in resObj || 'content' in resObj) &&
- 'tool_call_id' in resObj &&
- !('status' in resObj);
-
- const displayText = isToolAnswer
- ? resObj.answer || resObj.content || ''
- : statusText
- ? `${statusText}${bodyLength != null ? ` · ${bodyLength} byte` : ''}`
- : bodyLength != null
- ? `${bodyLength} byte`
- : String(textContent);
-
- return (
-
- ({
- bgcolor: byMode(theme, SURFACE_SUBTLE_PAIR),
- color: 'text.secondary',
- px: 1.25,
- py: 0.35,
- borderRadius: 2,
- fontSize: '0.72rem',
- maxWidth: '90%',
- fontFamily: 'monospace',
- border: `1px solid ${theme.palette.divider}`,
- display: 'inline-flex',
- alignItems: 'center',
- gap: 1,
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- })}
- >
-
- Ergebnis
-
-
- {displayText}
-
-
-
- );
- }
+ const isUser = msg.role === 'user';
+ const timeStr = showTime ? formatMessageTime(msg.created_at) : '';
return (