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
- const handleMouseMove = (event: MouseEvent) => {
 
 
 
 
 
 
 
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 and mouse
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 = event.clientY + tooltipHeight > viewportHeight;
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-hidden">
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
- <div className="flex-1 overflow-hidden flex flex-col">
345
- <Table className="w-full" style={{ minHeight: '100%', height: '100%'}}>
346
- <TableHeader className="sticky top-0 bg-background z-10 shadow-sm">
347
- <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)]">
348
- <TableHead>{t('documentPanel.documentManager.columns.id')}</TableHead>
349
- <TableHead>{t('documentPanel.documentManager.columns.summary')}</TableHead>
350
- <TableHead>{t('documentPanel.documentManager.columns.status')}</TableHead>
351
- <TableHead>{t('documentPanel.documentManager.columns.length')}</TableHead>
352
- <TableHead>{t('documentPanel.documentManager.columns.chunks')}</TableHead>
353
- <TableHead>{t('documentPanel.documentManager.columns.created')}</TableHead>
354
- <TableHead>{t('documentPanel.documentManager.columns.updated')}</TableHead>
355
- </TableRow>
356
- </TableHeader>
357
- <TableBody className="text-sm overflow-auto">
358
- {Object.entries(docs.statuses).map(([status, documents]) =>
359
- documents.map((doc) => (
360
- <TableRow key={doc.id}>
361
- <TableCell className="truncate font-mono overflow-visible max-w-[250px]">
362
- {showFileName ? (
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.id}
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
- </TableCell>
385
- <TableCell className="max-w-xs min-w-45 truncate overflow-visible">
386
  <div className="group relative overflow-visible tooltip-container">
387
  <div className="truncate">
388
- {doc.content_summary}
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.content_summary}
392
  </div>
393
  </div>
394
- </TableCell>
395
- <TableCell>
396
- {status === 'processed' && (
397
- <span className="text-green-600">{t('documentPanel.documentManager.status.completed')}</span>
398
- )}
399
- {status === 'processing' && (
400
- <span className="text-blue-600">{t('documentPanel.documentManager.status.processing')}</span>
401
- )}
402
- {status === 'pending' && <span className="text-yellow-600">{t('documentPanel.documentManager.status.pending')}</span>}
403
- {status === 'failed' && <span className="text-red-600">{t('documentPanel.documentManager.status.failed')}</span>}
404
- {doc.error && (
405
- <span className="ml-2 text-red-500" title={doc.error}>
406
- ⚠️
407
- </span>
408
- )}
409
- </TableCell>
410
- <TableCell>{doc.content_length ?? '-'}</TableCell>
411
- <TableCell>{doc.chunks_count ?? '-'}</TableCell>
412
- <TableCell className="truncate">
413
- {new Date(doc.created_at).toLocaleString()}
414
- </TableCell>
415
- <TableCell className="truncate">
416
- {new Date(doc.updated_at).toLocaleString()}
417
- </TableCell>
418
- </TableRow>
419
- ))
420
- )}
421
- </TableBody>
422
- </Table>
423
- </div>
 
 
 
 
 
 
 
 
 
 
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
  )}