yangdx
commited on
Commit
·
d28c92f
1
Parent(s):
91b3580
fix(ui): improve pipeline status dialog layout and styling
Browse files- Switch from AlertDialog to Dialog component for better modal behavior
- Adjust dialog positioning and alignment controls
- Remove custom close button to avoid duplication
- Add proper spacing between alignment buttons and close button
- Simplify history container height with min/max height
- Reduce overlay opacity for better visibility
lightrag_webui/src/components/documents/PipelineStatusDialog.tsx
CHANGED
@@ -1,16 +1,15 @@
|
|
1 |
-
import { useState, useEffect, useRef
|
2 |
import { useTranslation } from 'react-i18next'
|
3 |
import { toast } from 'sonner'
|
4 |
-
import {
|
5 |
|
6 |
import {
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
} from '@/components/ui/AlertDialog'
|
14 |
import Button from '@/components/ui/Button'
|
15 |
import { getPipelineStatus, PipelineStatusResponse } from '@/api/lightrag'
|
16 |
import { errorMessage } from '@/lib/utils'
|
@@ -31,44 +30,15 @@ export default function PipelineStatusDialog({
|
|
31 |
const [status, setStatus] = useState<PipelineStatusResponse | null>(null)
|
32 |
const [position, setPosition] = useState<DialogPosition>('center')
|
33 |
const [isUserScrolled, setIsUserScrolled] = useState(false)
|
34 |
-
const [historyHeight, setHistoryHeight] = useState('20em')
|
35 |
const historyRef = useRef<HTMLDivElement>(null)
|
36 |
-
const resizeObserverRef = useRef<ResizeObserver | null>(null)
|
37 |
-
|
38 |
-
// Calculate history height based on window height
|
39 |
-
const updateHistoryHeight = useCallback(() => {
|
40 |
-
const minHeight = 7.5 // 5 lines * 1.5em line height
|
41 |
-
const windowHeight = window.innerHeight
|
42 |
-
const pixelsPerEm = parseFloat(getComputedStyle(document.documentElement).fontSize)
|
43 |
-
const maxHeightInEm = Math.max(Math.floor((windowHeight * 0.4) / pixelsPerEm), minHeight)
|
44 |
-
setHistoryHeight(`${maxHeightInEm}em`)
|
45 |
-
}, [])
|
46 |
|
47 |
// Reset position when dialog opens
|
48 |
useEffect(() => {
|
49 |
if (open) {
|
50 |
setPosition('center')
|
51 |
setIsUserScrolled(false)
|
52 |
-
updateHistoryHeight()
|
53 |
-
}
|
54 |
-
}, [open, updateHistoryHeight])
|
55 |
-
|
56 |
-
// Setup resize observer
|
57 |
-
useEffect(() => {
|
58 |
-
if (!open) return
|
59 |
-
|
60 |
-
resizeObserverRef.current = new ResizeObserver((entries) => {
|
61 |
-
if (entries[0]) {
|
62 |
-
updateHistoryHeight()
|
63 |
-
}
|
64 |
-
})
|
65 |
-
|
66 |
-
resizeObserverRef.current.observe(document.body)
|
67 |
-
|
68 |
-
return () => {
|
69 |
-
resizeObserverRef.current?.disconnect()
|
70 |
}
|
71 |
-
}, [open
|
72 |
|
73 |
// Handle scroll position
|
74 |
useEffect(() => {
|
@@ -112,74 +82,63 @@ export default function PipelineStatusDialog({
|
|
112 |
}, [open, t])
|
113 |
|
114 |
return (
|
115 |
-
<
|
116 |
-
<
|
117 |
-
<AlertDialogContent
|
118 |
className={cn(
|
119 |
-
'sm:max-w-[600px] transition-all duration-200',
|
120 |
-
position === 'left' && '!left-
|
121 |
position === 'center' && '!left-1/2 !-translate-x-1/2',
|
122 |
-
position === 'right' && '!
|
123 |
)}
|
124 |
>
|
125 |
-
<
|
126 |
{status?.job_name
|
127 |
? `${t('documentPanel.pipelineStatus.jobName')}: ${status.job_name}, ${t('documentPanel.pipelineStatus.progress')}: ${status.cur_batch}/${status.batchs}`
|
128 |
: t('documentPanel.pipelineStatus.noActiveJob')
|
129 |
}
|
130 |
-
</
|
131 |
-
<
|
132 |
-
<
|
133 |
{t('documentPanel.pipelineStatus.title')}
|
134 |
-
</
|
135 |
|
136 |
-
{/* Position control buttons
|
137 |
-
<div className="flex items-center gap-2">
|
138 |
-
<
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
</Button>
|
161 |
-
<Button
|
162 |
-
variant="ghost"
|
163 |
-
size="icon"
|
164 |
-
className={cn(
|
165 |
-
'h-6 w-6',
|
166 |
-
position === 'right' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
|
167 |
-
)}
|
168 |
-
onClick={() => setPosition('right')}
|
169 |
-
>
|
170 |
-
<AlignRight className="h-4 w-4" />
|
171 |
-
</Button>
|
172 |
-
</div>
|
173 |
<Button
|
174 |
variant="ghost"
|
175 |
size="icon"
|
176 |
-
className=
|
177 |
-
|
|
|
|
|
|
|
178 |
>
|
179 |
-
<
|
180 |
</Button>
|
181 |
</div>
|
182 |
-
</
|
183 |
|
184 |
{/* Status Content */}
|
185 |
<div className="space-y-4 pt-4">
|
@@ -218,8 +177,7 @@ export default function PipelineStatusDialog({
|
|
218 |
<div
|
219 |
ref={historyRef}
|
220 |
onScroll={handleScroll}
|
221 |
-
className="font-mono text-sm rounded-md bg-zinc-800 text-zinc-100 p-3 overflow-y-auto"
|
222 |
-
style={{ height: historyHeight }}
|
223 |
>
|
224 |
{status?.history_messages?.map((msg, idx) => (
|
225 |
<div key={idx}>{msg}</div>
|
@@ -227,7 +185,7 @@ export default function PipelineStatusDialog({
|
|
227 |
</div>
|
228 |
</div>
|
229 |
</div>
|
230 |
-
</
|
231 |
-
</
|
232 |
)
|
233 |
}
|
|
|
1 |
+
import { useState, useEffect, useRef } from 'react'
|
2 |
import { useTranslation } from 'react-i18next'
|
3 |
import { toast } from 'sonner'
|
4 |
+
import { AlignLeft, AlignCenter, AlignRight } from 'lucide-react'
|
5 |
|
6 |
import {
|
7 |
+
Dialog,
|
8 |
+
DialogContent,
|
9 |
+
DialogHeader,
|
10 |
+
DialogTitle,
|
11 |
+
DialogDescription
|
12 |
+
} from '@/components/ui/Dialog'
|
|
|
13 |
import Button from '@/components/ui/Button'
|
14 |
import { getPipelineStatus, PipelineStatusResponse } from '@/api/lightrag'
|
15 |
import { errorMessage } from '@/lib/utils'
|
|
|
30 |
const [status, setStatus] = useState<PipelineStatusResponse | null>(null)
|
31 |
const [position, setPosition] = useState<DialogPosition>('center')
|
32 |
const [isUserScrolled, setIsUserScrolled] = useState(false)
|
|
|
33 |
const historyRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
// Reset position when dialog opens
|
36 |
useEffect(() => {
|
37 |
if (open) {
|
38 |
setPosition('center')
|
39 |
setIsUserScrolled(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
}
|
41 |
+
}, [open])
|
42 |
|
43 |
// Handle scroll position
|
44 |
useEffect(() => {
|
|
|
82 |
}, [open, t])
|
83 |
|
84 |
return (
|
85 |
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
86 |
+
<DialogContent
|
|
|
87 |
className={cn(
|
88 |
+
'sm:max-w-[600px] transition-all duration-200 fixed',
|
89 |
+
position === 'left' && '!left-[25%] !translate-x-[-50%] !mx-4',
|
90 |
position === 'center' && '!left-1/2 !-translate-x-1/2',
|
91 |
+
position === 'right' && '!left-[75%] !translate-x-[-50%] !mx-4'
|
92 |
)}
|
93 |
>
|
94 |
+
<DialogDescription className="sr-only">
|
95 |
{status?.job_name
|
96 |
? `${t('documentPanel.pipelineStatus.jobName')}: ${status.job_name}, ${t('documentPanel.pipelineStatus.progress')}: ${status.cur_batch}/${status.batchs}`
|
97 |
: t('documentPanel.pipelineStatus.noActiveJob')
|
98 |
}
|
99 |
+
</DialogDescription>
|
100 |
+
<DialogHeader className="flex flex-row items-center">
|
101 |
+
<DialogTitle className="flex-1">
|
102 |
{t('documentPanel.pipelineStatus.title')}
|
103 |
+
</DialogTitle>
|
104 |
|
105 |
+
{/* Position control buttons */}
|
106 |
+
<div className="flex items-center gap-2 mr-8">
|
107 |
+
<Button
|
108 |
+
variant="ghost"
|
109 |
+
size="icon"
|
110 |
+
className={cn(
|
111 |
+
'h-6 w-6',
|
112 |
+
position === 'left' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
|
113 |
+
)}
|
114 |
+
onClick={() => setPosition('left')}
|
115 |
+
>
|
116 |
+
<AlignLeft className="h-4 w-4" />
|
117 |
+
</Button>
|
118 |
+
<Button
|
119 |
+
variant="ghost"
|
120 |
+
size="icon"
|
121 |
+
className={cn(
|
122 |
+
'h-6 w-6',
|
123 |
+
position === 'center' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
|
124 |
+
)}
|
125 |
+
onClick={() => setPosition('center')}
|
126 |
+
>
|
127 |
+
<AlignCenter className="h-4 w-4" />
|
128 |
+
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
<Button
|
130 |
variant="ghost"
|
131 |
size="icon"
|
132 |
+
className={cn(
|
133 |
+
'h-6 w-6',
|
134 |
+
position === 'right' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
|
135 |
+
)}
|
136 |
+
onClick={() => setPosition('right')}
|
137 |
>
|
138 |
+
<AlignRight className="h-4 w-4" />
|
139 |
</Button>
|
140 |
</div>
|
141 |
+
</DialogHeader>
|
142 |
|
143 |
{/* Status Content */}
|
144 |
<div className="space-y-4 pt-4">
|
|
|
177 |
<div
|
178 |
ref={historyRef}
|
179 |
onScroll={handleScroll}
|
180 |
+
className="font-mono text-sm rounded-md bg-zinc-800 text-zinc-100 p-3 overflow-y-auto min-h-[7.5em] max-h-[40vh]"
|
|
|
181 |
>
|
182 |
{status?.history_messages?.map((msg, idx) => (
|
183 |
<div key={idx}>{msg}</div>
|
|
|
185 |
</div>
|
186 |
</div>
|
187 |
</div>
|
188 |
+
</DialogContent>
|
189 |
+
</Dialog>
|
190 |
)
|
191 |
}
|
lightrag_webui/src/components/ui/Dialog.tsx
CHANGED
@@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef<
|
|
19 |
<DialogPrimitive.Overlay
|
20 |
ref={ref}
|
21 |
className={cn(
|
22 |
-
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/
|
23 |
className
|
24 |
)}
|
25 |
{...props}
|
|
|
19 |
<DialogPrimitive.Overlay
|
20 |
ref={ref}
|
21 |
className={cn(
|
22 |
+
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/30',
|
23 |
className
|
24 |
)}
|
25 |
{...props}
|