diff --git a/src/components/ChatMessage.tsx b/src/components/ChatMessage.tsx index 6238730..49f3515 100644 --- a/src/components/ChatMessage.tsx +++ b/src/components/ChatMessage.tsx @@ -12,7 +12,7 @@ import { CodeBlock } from './CodeBlock'; import { ToolCall } from './ToolCall'; import { ImageBlock } from './ImageBlock'; import { buildImageSrc } from '../lib/image'; -import { Bot, User, Wrench, Copy, Check, RefreshCw, Zap, Info, Webhook } from 'lucide-react'; +import { Bot, User, Wrench, Copy, Check, RefreshCw, Zap, Info, Webhook, Braces } from 'lucide-react'; import { t, getLocale } from '../lib/i18n'; import { useLocale } from '../hooks/useLocale'; import { stripWebhookScaffolding, hasWebhookScaffolding } from '../lib/systemEvent'; @@ -293,6 +293,48 @@ function MetadataViewer({ metadata }: { metadata?: Record }) { ); } +function RawJsonToggle({ isOpen, onToggle }: { isOpen: boolean; onToggle: () => void }) { + return ( + + ); +} + +function RawJsonPanel({ message }: { message: ChatMessageType }) { + const [copied, setCopied] = useState(false); + const json = JSON.stringify(message, null, 2); + const handleCopy = useCallback(() => { + navigator.clipboard.writeText(json).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + }, [json]); + + return ( +
+
+ Raw JSON + +
+
+        {json}
+      
+
+ ); +} + /** Extract plain text from message blocks for clipboard copy */ function getPlainText(message: ChatMessageType): string { if (message.blocks.length > 0) { @@ -324,6 +366,7 @@ function SystemEventMessage({ message }: { message: ChatMessageType }) { export function ChatMessageComponent({ message: rawMessage, onRetry, agentAvatarUrl }: { message: ChatMessageType; onRetry?: (text: string) => void; agentAvatarUrl?: string }) { useLocale(); // re-render on locale change + const [showRawJson, setShowRawJson] = useState(false); // Strip webhook/hook scaffolding from user messages before rendering const message = useMemo(() => { @@ -396,6 +439,7 @@ export function ChatMessageComponent({ message: rawMessage, onRetry, agentAvatar )}
+ setShowRawJson(o => !o)} />
{/* Retry button (user messages only) */} {isUser && onRetry && ( @@ -437,6 +481,9 @@ export function ChatMessageComponent({ message: rawMessage, onRetry, agentAvatar {/* Tool calls & thinking (inline) */} {!isUser && } + + {/* Raw JSON viewer */} + {showRawJson && } {(message.timestamp || wasWebhookMessage) && (
diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index 608a3c5..0942a47 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -75,6 +75,8 @@ const en = { 'message.copied': 'Copied!', 'message.retry': 'Resend message', 'message.metadata': 'Message details', + 'message.rawJson': 'Raw JSON', + 'message.hideRawJson': 'Hide raw JSON', // Timestamps 'time.yesterday': 'Yesterday', @@ -179,6 +181,8 @@ const fr: Record = { 'message.copied': 'Copié !', 'message.retry': 'Renvoyer le message', 'message.metadata': 'Détails du message', + 'message.rawJson': 'JSON brut', + 'message.hideRawJson': 'Masquer le JSON brut', 'time.yesterday': 'Hier', 'time.today': "Aujourd'hui",