Skip to content

Commit 1d2c51d

Browse files
committed
feat: redesign log detail modal with improved UX
- Redesign log detail dialog with modern styling and better layout - Add copy buttons to User Input and Error sections - Fix text overflow issues in log content areas - Fix tab labels and content area width inconsistency - Remove duplicate close button (use DialogContent's built-in) - Add i18n translations for new UI elements (zh-CN, en-US) - Remove 200 character limit on user input for full content display - Use native scrolling instead of ScrollArea for consistent width Changes: - src/main/proxy/routes/chat.ts: Remove user input truncation - src/renderer/src/components/logs/RequestLogDetail.tsx: Complete redesign - src/renderer/src/components/logs/LogDetailModal.tsx: Simplify and fix overflow - src/renderer/src/i18n/locales/*.json: Add new translation keys
1 parent 4c80743 commit 1d2c51d

File tree

5 files changed

+300
-182
lines changed

5 files changed

+300
-182
lines changed

src/main/proxy/routes/chat.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function getClientIP(ctx: Context): string {
3939
}
4040

4141
/**
42-
* Extract user input from messages (last user message, truncated to 200 chars)
42+
* Extract user input from messages (last user message, full content)
4343
*/
4444
function extractUserInput(messages: Array<{ role: string; content?: string | any[] | null }>): string | undefined {
4545
for (let i = messages.length - 1; i >= 0; i--) {
@@ -55,7 +55,7 @@ function extractUserInput(messages: Array<{ role: string; content?: string | any
5555
}
5656
}
5757
if (content) {
58-
return content.length > 200 ? content.substring(0, 200) + '...' : content
58+
return content
5959
}
6060
}
6161
}

src/renderer/src/components/logs/LogDetailModal.tsx

Lines changed: 31 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ interface LogDetailModalProps {
3838
onClose: () => void
3939
}
4040

41-
function JsonViewer({ data, maxHeight = 300 }: { data: string; maxHeight?: number }) {
41+
function JsonViewer({ data }: { data: string }) {
4242
const [copied, setCopied] = useState(false)
43-
const [expanded, setExpanded] = useState(false)
4443

4544
const handleCopy = useCallback(async () => {
4645
try {
@@ -52,64 +51,14 @@ function JsonViewer({ data, maxHeight = 300 }: { data: string; maxHeight?: numbe
5251
}
5352
}, [data])
5453

55-
const lines = data.split('\n')
56-
const lineCount = lines.length
57-
const isLong = lineCount > 20 || data.length > 2000
58-
59-
const renderLine = (line: string, index: number) => {
60-
let className = 'text-muted-foreground'
61-
62-
if (line.includes(':')) {
63-
const valueParts = line.split(':')
64-
const value = valueParts.slice(1).join(':').trim()
65-
66-
if (value.startsWith('"') || value.startsWith("'")) {
67-
className = 'text-green-500'
68-
} else if (value === 'true' || value === 'false') {
69-
className = 'text-yellow-500'
70-
} else if (!isNaN(Number(value))) {
71-
className = 'text-blue-500'
72-
} else if (value === 'null') {
73-
className = 'text-red-400'
74-
}
75-
}
76-
77-
return (
78-
<div key={index} className="flex">
79-
<span className="w-8 text-right pr-2 text-muted-foreground/50 select-none text-xs">
80-
{index + 1}
81-
</span>
82-
<span className={cn('text-xs font-mono', className)}>{line}</span>
83-
</div>
84-
)
85-
}
86-
8754
return (
8855
<div className="relative">
89-
<div
90-
className={cn('bg-muted/50 rounded-lg p-3 overflow-auto', !expanded && isLong)}
91-
style={{ maxHeight: expanded ? undefined : maxHeight }}
92-
>
93-
<pre className="text-xs font-mono">
94-
{lines.slice(0, expanded ? undefined : 20).map(renderLine)}
95-
{!expanded && isLong && (
96-
<div className="text-muted-foreground text-center py-2">
97-
... {lineCount - 20} more lines
98-
</div>
99-
)}
56+
<div className="bg-muted/50 rounded-lg p-3 overflow-x-auto">
57+
<pre className="text-xs font-mono whitespace-pre-wrap break-all">
58+
{data}
10059
</pre>
10160
</div>
102-
<div className="absolute top-2 right-2 flex gap-1">
103-
{isLong && (
104-
<Button
105-
variant="ghost"
106-
size="sm"
107-
className="h-6 px-2 text-xs"
108-
onClick={() => setExpanded(!expanded)}
109-
>
110-
{expanded ? 'Collapse' : 'Expand'}
111-
</Button>
112-
)}
61+
<div className="absolute top-2 right-2">
11362
<Button
11463
variant="ghost"
11564
size="sm"
@@ -140,9 +89,9 @@ export function LogDetailModal({ log, open, onClose }: LogDetailModalProps) {
14089

14190
return (
14291
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
143-
<DialogContent className="max-w-3xl max-h-[80vh]">
144-
<DialogHeader>
145-
<DialogTitle className="flex items-center justify-between">
92+
<DialogContent className="max-w-3xl w-[90vw] h-[85vh] max-h-[800px] p-0 flex flex-col">
93+
<DialogHeader className="px-6 pt-6 pb-4 shrink-0">
94+
<DialogTitle className="flex items-center justify-between flex-wrap gap-2">
14695
<span>{t('logs.detail')}</span>
14796
<div className="flex gap-2">
14897
<Button variant="outline" size="sm" onClick={handleExport}>
@@ -155,38 +104,38 @@ export function LogDetailModal({ log, open, onClose }: LogDetailModalProps) {
155104
</div>
156105
</DialogTitle>
157106
</DialogHeader>
158-
<ScrollArea className="flex-1">
159-
<div className="space-y-4">
107+
<ScrollArea className="flex-1 px-6 pb-6 min-h-0">
108+
<div className="space-y-4 pr-4">
160109
<div className="grid grid-cols-2 gap-4">
161-
<div>
110+
<div className="min-w-0">
162111
<label className="text-sm text-muted-foreground">{t('logs.time')}</label>
163-
<p className="font-medium">{log.timestamp}</p>
112+
<p className="font-medium break-all">{log.timestamp}</p>
164113
</div>
165-
<div>
114+
<div className="min-w-0">
166115
<label className="text-sm text-muted-foreground">{t('logs.status')}</label>
167-
<p className="font-medium flex items-center gap-2">
116+
<p className="font-medium flex items-center gap-2 flex-wrap">
168117
<span
169118
className={cn(
170-
'h-2 w-2 rounded-full',
119+
'h-2 w-2 rounded-full shrink-0',
171120
log.status === 'success' ? 'bg-green-500' : 'bg-red-500'
172121
)}
173122
/>
174-
{log.statusCode}
123+
<span className="break-all">{log.statusCode}</span>
175124
</p>
176125
</div>
177-
<div>
126+
<div className="min-w-0">
178127
<label className="text-sm text-muted-foreground">{t('logs.provider')}</label>
179-
<p className="font-medium">{log.provider || '-'}</p>
128+
<p className="font-medium break-all">{log.provider || '-'}</p>
180129
</div>
181-
<div>
130+
<div className="min-w-0">
182131
<label className="text-sm text-muted-foreground">{t('logs.model')}</label>
183-
<p className="font-medium">{log.model || '-'}</p>
132+
<p className="font-medium break-all">{log.model || '-'}</p>
184133
</div>
185-
<div>
134+
<div className="min-w-0">
186135
<label className="text-sm text-muted-foreground">{t('logs.account')}</label>
187-
<p className="font-medium">{log.account || '-'}</p>
136+
<p className="font-medium break-all">{log.account || '-'}</p>
188137
</div>
189-
<div>
138+
<div className="min-w-0">
190139
<label className="text-sm text-muted-foreground">Duration</label>
191140
<p className="font-medium">{log.duration ? `${(log.duration * 1000).toFixed(0)}ms` : '-'}</p>
192141
</div>
@@ -195,17 +144,21 @@ export function LogDetailModal({ log, open, onClose }: LogDetailModalProps) {
195144
{log.userInput && (
196145
<div>
197146
<label className="text-sm text-muted-foreground">{t('logs.userInput')}</label>
198-
<div className="mt-1 p-3 bg-muted/50 rounded-lg overflow-auto max-h-[200px]">
199-
<p className="text-sm whitespace-pre-wrap break-all">{log.userInput}</p>
147+
<div className="mt-1 p-3 bg-muted/50 rounded-lg">
148+
<pre className="text-sm whitespace-pre-wrap break-all font-sans">
149+
{log.userInput}
150+
</pre>
200151
</div>
201152
</div>
202153
)}
203154

204155
{log.error && (
205156
<div>
206157
<label className="text-sm text-red-500">Error</label>
207-
<div className="mt-1 p-3 bg-red-500/10 rounded-lg border border-red-500/20 overflow-auto max-h-[200px]">
208-
<p className="text-sm text-red-600 whitespace-pre-wrap break-all">{log.error}</p>
158+
<div className="mt-1 p-3 bg-red-500/10 rounded-lg border border-red-500/20">
159+
<pre className="text-sm text-red-600 whitespace-pre-wrap break-all font-sans">
160+
{log.error}
161+
</pre>
209162
</div>
210163
</div>
211164
)}

0 commit comments

Comments
 (0)