yangdx
commited on
Commit
·
fc19f02
1
Parent(s):
cfaf328
Optimize tooltips and layout
Browse files
lightrag_webui/src/features/DocumentManager.tsx
CHANGED
|
@@ -47,14 +47,6 @@ const getDisplayFileName = (doc: DocStatusResponse, maxLength: number = 20): str
|
|
| 47 |
};
|
| 48 |
|
| 49 |
const pulseStyle = `
|
| 50 |
-
/* Custom tooltip styles */
|
| 51 |
-
.tooltip-top {
|
| 52 |
-
bottom: 100% !important;
|
| 53 |
-
top: auto !important;
|
| 54 |
-
margin-bottom: 0.25rem !important;
|
| 55 |
-
margin-top: 0 !important;
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
/* Fixed tooltip styles for small tables */
|
| 59 |
.tooltip-fixed {
|
| 60 |
position: fixed !important;
|
|
@@ -142,13 +134,21 @@ export default function DocumentManager() {
|
|
| 142 |
useEffect(() => {
|
| 143 |
if (!docs) return;
|
| 144 |
|
| 145 |
-
// Function to handle mouse movement
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
const cardContent = cardContentRef.current;
|
| 148 |
if (!cardContent) return;
|
| 149 |
|
| 150 |
// Get all visible tooltips
|
| 151 |
const visibleTooltips = document.querySelectorAll<HTMLElement>('.group:hover > div[class*="invisible group-hover:visible absolute"]');
|
|
|
|
| 152 |
|
| 153 |
visibleTooltips.forEach(tooltip => {
|
| 154 |
// Get the parent element that triggered the tooltip
|
|
@@ -160,12 +160,12 @@ export default function DocumentManager() {
|
|
| 160 |
// Use fixed positioning for all tooltips
|
| 161 |
tooltip.classList.add('tooltip-fixed');
|
| 162 |
|
| 163 |
-
// Calculate position based on trigger element
|
| 164 |
const tooltipHeight = tooltip.offsetHeight;
|
| 165 |
const viewportHeight = window.innerHeight;
|
| 166 |
|
| 167 |
// Check if tooltip would go off the bottom of the viewport
|
| 168 |
-
const wouldOverflowBottom =
|
| 169 |
|
| 170 |
if (wouldOverflowBottom) {
|
| 171 |
// Position above the trigger
|
|
@@ -273,7 +273,7 @@ export default function DocumentManager() {
|
|
| 273 |
<CardHeader className="py-2 px-6">
|
| 274 |
<CardTitle className="text-lg">{t('documentPanel.documentManager.title')}</CardTitle>
|
| 275 |
</CardHeader>
|
| 276 |
-
<CardContent className="flex-1 flex flex-col min-h-0 overflow-
|
| 277 |
<div className="flex gap-2 mb-2">
|
| 278 |
<div className="flex gap-2">
|
| 279 |
<Button
|
|
@@ -340,87 +340,85 @@ export default function DocumentManager() {
|
|
| 340 |
)}
|
| 341 |
{docs && (
|
| 342 |
<div className="absolute inset-0 flex flex-col p-0">
|
| 343 |
-
<div className="w-full h-full flex flex-col rounded-lg border border-gray-200 dark:border-gray-700">
|
| 344 |
-
<
|
| 345 |
-
<
|
| 346 |
-
<
|
| 347 |
-
<
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
<
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
<>
|
| 364 |
-
<div className="group relative overflow-visible tooltip-container">
|
| 365 |
-
<div className="truncate">
|
| 366 |
-
{getDisplayFileName(doc, 30)}
|
| 367 |
-
</div>
|
| 368 |
-
<div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[600px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
|
| 369 |
-
{doc.file_path}
|
| 370 |
-
</div>
|
| 371 |
-
</div>
|
| 372 |
-
<div className="text-xs text-gray-500">{doc.id}</div>
|
| 373 |
-
</>
|
| 374 |
-
) : (
|
| 375 |
<div className="group relative overflow-visible tooltip-container">
|
| 376 |
<div className="truncate">
|
| 377 |
-
{doc
|
| 378 |
</div>
|
| 379 |
<div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[600px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
|
| 380 |
{doc.file_path}
|
| 381 |
</div>
|
| 382 |
</div>
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
<div className="group relative overflow-visible tooltip-container">
|
| 387 |
<div className="truncate">
|
| 388 |
-
{doc.
|
| 389 |
</div>
|
| 390 |
<div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[600px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
|
| 391 |
-
{doc.
|
| 392 |
</div>
|
| 393 |
</div>
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
)}
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
</
|
| 415 |
-
|
| 416 |
-
{
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
</div>
|
| 425 |
</div>
|
| 426 |
)}
|
|
|
|
| 47 |
};
|
| 48 |
|
| 49 |
const pulseStyle = `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
/* Fixed tooltip styles for small tables */
|
| 51 |
.tooltip-fixed {
|
| 52 |
position: fixed !important;
|
|
|
|
| 134 |
useEffect(() => {
|
| 135 |
if (!docs) return;
|
| 136 |
|
| 137 |
+
// Function to handle mouse movement - throttled to reduce layout calculations
|
| 138 |
+
let lastExecution = 0;
|
| 139 |
+
const throttleInterval = 50; // ms
|
| 140 |
+
|
| 141 |
+
const handleMouseMove = () => {
|
| 142 |
+
const now = Date.now();
|
| 143 |
+
if (now - lastExecution < throttleInterval) return;
|
| 144 |
+
lastExecution = now;
|
| 145 |
+
|
| 146 |
const cardContent = cardContentRef.current;
|
| 147 |
if (!cardContent) return;
|
| 148 |
|
| 149 |
// Get all visible tooltips
|
| 150 |
const visibleTooltips = document.querySelectorAll<HTMLElement>('.group:hover > div[class*="invisible group-hover:visible absolute"]');
|
| 151 |
+
if (visibleTooltips.length === 0) return;
|
| 152 |
|
| 153 |
visibleTooltips.forEach(tooltip => {
|
| 154 |
// Get the parent element that triggered the tooltip
|
|
|
|
| 160 |
// Use fixed positioning for all tooltips
|
| 161 |
tooltip.classList.add('tooltip-fixed');
|
| 162 |
|
| 163 |
+
// Calculate position based on trigger element
|
| 164 |
const tooltipHeight = tooltip.offsetHeight;
|
| 165 |
const viewportHeight = window.innerHeight;
|
| 166 |
|
| 167 |
// Check if tooltip would go off the bottom of the viewport
|
| 168 |
+
const wouldOverflowBottom = triggerRect.bottom + tooltipHeight + 5 > viewportHeight;
|
| 169 |
|
| 170 |
if (wouldOverflowBottom) {
|
| 171 |
// Position above the trigger
|
|
|
|
| 273 |
<CardHeader className="py-2 px-6">
|
| 274 |
<CardTitle className="text-lg">{t('documentPanel.documentManager.title')}</CardTitle>
|
| 275 |
</CardHeader>
|
| 276 |
+
<CardContent className="flex-1 flex flex-col min-h-0 overflow-auto">
|
| 277 |
<div className="flex gap-2 mb-2">
|
| 278 |
<div className="flex gap-2">
|
| 279 |
<Button
|
|
|
|
| 340 |
)}
|
| 341 |
{docs && (
|
| 342 |
<div className="absolute inset-0 flex flex-col p-0">
|
| 343 |
+
<div className="w-full h-full flex flex-col rounded-lg border border-gray-200 dark:border-gray-700 overflow-auto">
|
| 344 |
+
<Table className="w-full">
|
| 345 |
+
<TableHeader className="sticky top-0 bg-background z-10 shadow-sm">
|
| 346 |
+
<TableRow className="border-b bg-card/95 backdrop-blur supports-[backdrop-filter]:bg-card/75 shadow-[inset_0_-1px_0_rgba(0,0,0,0.1)]">
|
| 347 |
+
<TableHead>{t('documentPanel.documentManager.columns.id')}</TableHead>
|
| 348 |
+
<TableHead>{t('documentPanel.documentManager.columns.summary')}</TableHead>
|
| 349 |
+
<TableHead>{t('documentPanel.documentManager.columns.status')}</TableHead>
|
| 350 |
+
<TableHead>{t('documentPanel.documentManager.columns.length')}</TableHead>
|
| 351 |
+
<TableHead>{t('documentPanel.documentManager.columns.chunks')}</TableHead>
|
| 352 |
+
<TableHead>{t('documentPanel.documentManager.columns.created')}</TableHead>
|
| 353 |
+
<TableHead>{t('documentPanel.documentManager.columns.updated')}</TableHead>
|
| 354 |
+
</TableRow>
|
| 355 |
+
</TableHeader>
|
| 356 |
+
<TableBody className="text-sm overflow-auto">
|
| 357 |
+
{Object.entries(docs.statuses).map(([status, documents]) =>
|
| 358 |
+
documents.map((doc) => (
|
| 359 |
+
<TableRow key={doc.id}>
|
| 360 |
+
<TableCell className="truncate font-mono overflow-visible max-w-[250px]">
|
| 361 |
+
{showFileName ? (
|
| 362 |
+
<>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
<div className="group relative overflow-visible tooltip-container">
|
| 364 |
<div className="truncate">
|
| 365 |
+
{getDisplayFileName(doc, 30)}
|
| 366 |
</div>
|
| 367 |
<div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[600px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
|
| 368 |
{doc.file_path}
|
| 369 |
</div>
|
| 370 |
</div>
|
| 371 |
+
<div className="text-xs text-gray-500">{doc.id}</div>
|
| 372 |
+
</>
|
| 373 |
+
) : (
|
| 374 |
<div className="group relative overflow-visible tooltip-container">
|
| 375 |
<div className="truncate">
|
| 376 |
+
{doc.id}
|
| 377 |
</div>
|
| 378 |
<div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[600px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
|
| 379 |
+
{doc.file_path}
|
| 380 |
</div>
|
| 381 |
</div>
|
| 382 |
+
)}
|
| 383 |
+
</TableCell>
|
| 384 |
+
<TableCell className="max-w-xs min-w-45 truncate overflow-visible">
|
| 385 |
+
<div className="group relative overflow-visible tooltip-container">
|
| 386 |
+
<div className="truncate">
|
| 387 |
+
{doc.content_summary}
|
| 388 |
+
</div>
|
| 389 |
+
<div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[600px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
|
| 390 |
+
{doc.content_summary}
|
| 391 |
+
</div>
|
| 392 |
+
</div>
|
| 393 |
+
</TableCell>
|
| 394 |
+
<TableCell>
|
| 395 |
+
{status === 'processed' && (
|
| 396 |
+
<span className="text-green-600">{t('documentPanel.documentManager.status.completed')}</span>
|
| 397 |
+
)}
|
| 398 |
+
{status === 'processing' && (
|
| 399 |
+
<span className="text-blue-600">{t('documentPanel.documentManager.status.processing')}</span>
|
| 400 |
+
)}
|
| 401 |
+
{status === 'pending' && <span className="text-yellow-600">{t('documentPanel.documentManager.status.pending')}</span>}
|
| 402 |
+
{status === 'failed' && <span className="text-red-600">{t('documentPanel.documentManager.status.failed')}</span>}
|
| 403 |
+
{doc.error && (
|
| 404 |
+
<span className="ml-2 text-red-500" title={doc.error}>
|
| 405 |
+
⚠️
|
| 406 |
+
</span>
|
| 407 |
+
)}
|
| 408 |
+
</TableCell>
|
| 409 |
+
<TableCell>{doc.content_length ?? '-'}</TableCell>
|
| 410 |
+
<TableCell>{doc.chunks_count ?? '-'}</TableCell>
|
| 411 |
+
<TableCell className="truncate">
|
| 412 |
+
{new Date(doc.created_at).toLocaleString()}
|
| 413 |
+
</TableCell>
|
| 414 |
+
<TableCell className="truncate">
|
| 415 |
+
{new Date(doc.updated_at).toLocaleString()}
|
| 416 |
+
</TableCell>
|
| 417 |
+
</TableRow>
|
| 418 |
+
))
|
| 419 |
+
)}
|
| 420 |
+
</TableBody>
|
| 421 |
+
</Table>
|
| 422 |
</div>
|
| 423 |
</div>
|
| 424 |
)}
|