feat: enhance Message Metadata Dialog with structured display

Previously the dialog simply rendered the raw JSON of the metadata, which made it hard to read and required the CodeEditor dependency. This change replaces the raw viewer with a set of semantic sections that show assistant details, model parameters, token speed, and timestamps in a clean, icon‑rich layout. The component now uses TypeScript interfaces for better type safety, memoized formatting helpers, and removes the unnecessary CodeEditor import. Locale entries were added for all new labels.

The updated UI improves user experience by making metadata more accessible and readable, while simplifying the code base and reducing bundle size.
This commit is contained in:
Akarshan 2025-10-30 12:09:02 +05:30
parent 89d158dc8b
commit 98d81819c5
No known key found for this signature in database
GPG Key ID: D75C9634A870665F
2 changed files with 215 additions and 24 deletions

View File

@ -1,4 +1,4 @@
import { useState } from 'react'
import { useState, useMemo } from 'react'
import { useTranslation } from '@/i18n/react-i18next-compat'
import {
Dialog,
@ -7,21 +7,96 @@ import {
DialogTitle,
DialogHeader,
} from '@/components/ui/dialog'
import { IconInfoCircle } from '@tabler/icons-react'
import {
IconInfoCircle,
IconRobot,
IconGauge,
IconId,
IconCalendar,
IconTemperature,
IconHierarchy,
IconTool,
IconBoxMultiple,
IconRuler,
IconMessageCircle,
} from '@tabler/icons-react'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip'
import CodeEditor from '@uiw/react-textarea-code-editor'
import '@uiw/react-textarea-code-editor/dist.css'
// Removed CodeEditor and its styles
// Type definitions for the provided metadata structure
interface Parameters {
temperature: number
top_k: number
top_p: number
}
interface AssistantMetadata {
avatar: string
created_at: number
description: string
id: string
instructions: string
name: string
parameters: Parameters
tool_steps: number
}
interface TokenSpeedMetadata {
lastTimestamp: number
message: string
tokenCount: number
tokenSpeed: number
}
interface MessageMetadata {
assistant?: AssistantMetadata
tokenSpeed?: TokenSpeedMetadata
}
interface MessageMetadataDialogProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
metadata: any
metadata: MessageMetadata // Use the specific interface
triggerElement?: React.ReactNode
}
// --- Helper Components & Utilities ---
// A utility component to display a single detail row
const DetailItem: React.FC<{
icon: React.ReactNode
label: string
value: React.ReactNode
}> = ({ icon, label, value }) => (
<div className="flex items-start text-sm p-2 bg-main-view-bg/5 rounded-md">
<div className="text-accent mr-3 flex-shrink-0">{icon}</div>
<div className="flex flex-col">
<span className="font-semibold text-main-view-fg/80">{label}:</span>
<span className="text-main-view-fg/90 whitespace-pre-wrap break-words">
{value}
</span>
</div>
</div>
)
// Helper for formatting timestamps
const formatDate = (timestamp: number) => {
if (!timestamp) return 'N/A'
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZoneName: 'short',
}).format(new Date(timestamp))
}
// --- Main Component ---
export function MessageMetadataDialog({
metadata,
triggerElement,
@ -29,10 +104,12 @@ export function MessageMetadataDialog({
const { t } = useTranslation()
const [isOpen, setIsOpen] = useState(false)
const { assistant, tokenSpeed } = (metadata || {}) as MessageMetadata
const defaultTrigger = (
<Tooltip>
<TooltipTrigger asChild>
<div
<div
className="outline-0 focus:outline-0 flex items-center gap-1 hover:text-accent transition-colors cursor-pointer group relative"
role="button"
tabIndex={0}
@ -52,27 +129,127 @@ export function MessageMetadataDialog({
</Tooltip>
)
const formattedTokenSpeed = useMemo(() => {
if (tokenSpeed?.tokenSpeed === undefined) return 'N/A'
return (
new Intl.NumberFormat('en-US', {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(tokenSpeed.tokenSpeed) + ' tokens/s'
)
}, [tokenSpeed])
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger>{triggerElement || defaultTrigger}</DialogTrigger>
<DialogContent>
<DialogContent className="max-w-xl">
<DialogHeader>
<DialogTitle>{t('common:dialogs.messageMetadata.title')}</DialogTitle>
<div className="space-y-2 mt-4">
<div className="border border-main-view-fg/10 rounded-md">
<CodeEditor
value={JSON.stringify(metadata || {}, null, 2)}
language="json"
readOnly
data-color-mode="dark"
style={{
fontSize: 12,
backgroundColor: 'transparent',
fontFamily: 'monospace',
}}
className="w-full h-full !text-sm "
/>
</div>
<div className="space-y-6 mt-4 max-h-[70vh] overflow-y-auto pr-2">
{/* --- Assistant/Model Section --- */}
{assistant && (
<section>
<h3 className="flex items-center text-lg font-bold border-b border-main-view-fg/10 pb-2 mb-3">
<IconRobot className="mr-2" size={20} />
{t('common:dialogs.messageMetadata.model')}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<DetailItem
icon={<IconRobot size={18} />}
label={t('common:dialogs.messageMetadata.name')}
value={`${assistant.avatar} ${assistant.name}`}
/>
<DetailItem
icon={<IconId size={18} />}
label={t('common:dialogs.messageMetadata.id')}
value={assistant.id}
/>
<DetailItem
icon={<IconCalendar size={18} />}
label={t('common:dialogs.messageMetadata.createdAt')}
value={formatDate(assistant.created_at)}
/>
<DetailItem
icon={<IconTool size={18} />}
label={t('common:dialogs.messageMetadata.toolSteps')}
value={assistant.tool_steps}
/>
{/* Parameters */}
<div className="col-span-1 md:col-span-2 grid grid-cols-3 gap-3">
<DetailItem
icon={<IconTemperature size={18} />}
label={t('common:dialogs.messageMetadata.temperature')}
value={assistant.parameters.temperature}
/>
<DetailItem
icon={<IconHierarchy size={18} />}
label={t('common:dialogs.messageMetadata.topK')}
value={assistant.parameters.top_k}
/>
<DetailItem
icon={<IconBoxMultiple size={18} />}
label={t('common:dialogs.messageMetadata.topP')}
value={assistant.parameters.top_p}
/>
</div>
{/* Description/Instructions */}
{(assistant.description || assistant.instructions) && (
<div className="col-span-1 md:col-span-2 space-y-3">
{assistant.description && (
<DetailItem
icon={<IconMessageCircle size={18} />}
label={t('common:dialogs.messageMetadata.description')}
value={assistant.description}
/>
)}
{assistant.instructions && (
<DetailItem
icon={<IconMessageCircle size={18} />}
label={t('common:dialogs.messageMetadata.instructions')}
value={assistant.instructions}
/>
)}
</div>
)}
</div>
</section>
)}
{/* --- Token Speed Section --- */}
{tokenSpeed && (
<section>
<h3 className="flex items-center text-lg font-bold border-b border-main-view-fg/10 pb-2 mb-3">
<IconGauge className="mr-2" size={20} />
{t('Performance')}
</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
<DetailItem
icon={<IconGauge size={18} />}
label={t('common:dialogs.messageMetadata.tokenSpeed')}
value={formattedTokenSpeed}
/>
<DetailItem
icon={<IconRuler size={18} />}
label={t('common:dialogs.messageMetadata.tokenCount')}
value={tokenSpeed.tokenCount}
/>
<DetailItem
icon={<IconCalendar size={18} />}
label={t('common:dialogs.messageMetadata.lastUpdate')}
value={formatDate(tokenSpeed.lastTimestamp)}
/>
</div>
</section>
)}
{!assistant && !tokenSpeed && (
<p className="text-center text-main-view-fg/70 py-4">
{t('common:dialogs.messageMetadata.noMetadataAvailable.')}
</p>
)}
</div>
</DialogHeader>
</DialogContent>

View File

@ -235,7 +235,21 @@
"title": "Edit Message"
},
"messageMetadata": {
"title": "Message Metadata"
"title": "Message Metadata",
"model": "Model",
"name": "Name",
"id": "ID",
"createdAt": "Created At",
"toolSteps": "Tool Steps",
"temperature": "Temperature",
"topK": "Top K",
"topP": "Top P",
"description": "Description",
"instructions": "Instructions",
"tokenSpeed": "Token Speed",
"tokenCount": "Token Count",
"lastUpdate": "Last Update",
"noMessageMetadataAvailable": "No Message Metadata Available"
}
},
"projects": {