mirror of
				https://github.com/HKUDS/LightRAG.git
				synced 2025-11-03 19:29:38 +00:00 
			
		
		
		
	Merge pull request #1198 from danielaskdd/main
Improve document TAB layout and tooltips handling logic
This commit is contained in:
		
						commit
						c22076b5aa
					
				
							
								
								
									
										1
									
								
								lightrag/api/webui/assets/index-CP4Boz-Y.css
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lightrag/api/webui/assets/index-CP4Boz-Y.css
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								lightrag/api/webui/assets/index-CbzkrOyx.css
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								lightrag/api/webui/assets/index-CbzkrOyx.css
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										4
									
								
								lightrag/api/webui/index.html
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								lightrag/api/webui/index.html
									
									
									
										generated
									
									
									
								
							@ -8,8 +8,8 @@
 | 
			
		||||
    <link rel="icon" type="image/svg+xml" href="logo.png" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Lightrag</title>
 | 
			
		||||
    <script type="module" crossorigin src="/webui/assets/index-DTDDxtXc.js"></script>
 | 
			
		||||
    <link rel="stylesheet" crossorigin href="/webui/assets/index-CbzkrOyx.css">
 | 
			
		||||
    <script type="module" crossorigin src="/webui/assets/index-CZkfsko8.js"></script>
 | 
			
		||||
    <link rel="stylesheet" crossorigin href="/webui/assets/index-CP4Boz-Y.css">
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="root"></div>
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,18 @@ const getDisplayFileName = (doc: DocStatusResponse, maxLength: number = 20): str
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const pulseStyle = `
 | 
			
		||||
/* Fixed tooltip styles for small tables */
 | 
			
		||||
.tooltip-fixed {
 | 
			
		||||
  position: fixed !important;
 | 
			
		||||
  z-index: 9999 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parent container for tooltips */
 | 
			
		||||
.tooltip-container {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  overflow: visible !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes pulse {
 | 
			
		||||
  0% {
 | 
			
		||||
    background-color: rgb(255 0 0 / 0.1);
 | 
			
		||||
@ -115,6 +127,70 @@ export default function DocumentManager() {
 | 
			
		||||
    }
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  // Reference to the card content element
 | 
			
		||||
  const cardContentRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
 | 
			
		||||
  // Add tooltip position adjustment based on mouse position
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!docs) return;
 | 
			
		||||
 | 
			
		||||
    // Function to handle mouse movement - throttled to reduce layout calculations
 | 
			
		||||
    let lastExecution = 0;
 | 
			
		||||
    const throttleInterval = 50; // ms
 | 
			
		||||
 | 
			
		||||
    const handleMouseMove = () => {
 | 
			
		||||
      const now = Date.now();
 | 
			
		||||
      if (now - lastExecution < throttleInterval) return;
 | 
			
		||||
      lastExecution = now;
 | 
			
		||||
 | 
			
		||||
      const cardContent = cardContentRef.current;
 | 
			
		||||
      if (!cardContent) return;
 | 
			
		||||
 | 
			
		||||
      // Get all visible tooltips
 | 
			
		||||
      const visibleTooltips = document.querySelectorAll<HTMLElement>('.group:hover > div[class*="invisible group-hover:visible absolute"]');
 | 
			
		||||
      if (visibleTooltips.length === 0) return;
 | 
			
		||||
 | 
			
		||||
      visibleTooltips.forEach(tooltip => {
 | 
			
		||||
        // Get the parent element that triggered the tooltip
 | 
			
		||||
        const triggerElement = tooltip.parentElement;
 | 
			
		||||
        if (!triggerElement) return;
 | 
			
		||||
 | 
			
		||||
        const triggerRect = triggerElement.getBoundingClientRect();
 | 
			
		||||
 | 
			
		||||
        // Use fixed positioning for all tooltips
 | 
			
		||||
        tooltip.classList.add('tooltip-fixed');
 | 
			
		||||
 | 
			
		||||
        // Calculate position based on trigger element
 | 
			
		||||
        const tooltipHeight = tooltip.offsetHeight;
 | 
			
		||||
        const viewportHeight = window.innerHeight;
 | 
			
		||||
 | 
			
		||||
        // Check if tooltip would go off the bottom of the viewport
 | 
			
		||||
        const wouldOverflowBottom = triggerRect.bottom + tooltipHeight + 5 > viewportHeight;
 | 
			
		||||
 | 
			
		||||
        if (wouldOverflowBottom) {
 | 
			
		||||
          // Position above the trigger
 | 
			
		||||
          tooltip.style.top = `${triggerRect.top - tooltipHeight - 5}px`;
 | 
			
		||||
          tooltip.style.bottom = 'auto';
 | 
			
		||||
        } else {
 | 
			
		||||
          // Position below the trigger
 | 
			
		||||
          tooltip.style.top = `${triggerRect.bottom + 5}px`;
 | 
			
		||||
          tooltip.style.bottom = 'auto';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Horizontal positioning
 | 
			
		||||
        tooltip.style.left = `${triggerRect.left}px`;
 | 
			
		||||
        tooltip.style.maxWidth = '600px';
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Add mouse move listener to the document
 | 
			
		||||
    document.addEventListener('mousemove', handleMouseMove);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      document.removeEventListener('mousemove', handleMouseMove);
 | 
			
		||||
    };
 | 
			
		||||
  }, [docs]);
 | 
			
		||||
 | 
			
		||||
  const fetchDocuments = useCallback(async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      const docs = await getDocuments()
 | 
			
		||||
@ -193,12 +269,12 @@ export default function DocumentManager() {
 | 
			
		||||
  }, [health, fetchDocuments, t, currentTab])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="!size-full !rounded-none !border-none">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
    <Card className="!rounded-none !overflow-hidden flex flex-col h-full min-h-0">
 | 
			
		||||
      <CardHeader className="py-2 px-6">
 | 
			
		||||
        <CardTitle className="text-lg">{t('documentPanel.documentManager.title')}</CardTitle>
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardContent className="space-y-4">
 | 
			
		||||
        <div className="flex gap-2">
 | 
			
		||||
      <CardContent className="flex-1 flex flex-col min-h-0 overflow-auto">
 | 
			
		||||
        <div className="flex gap-2 mb-2">
 | 
			
		||||
          <div className="flex gap-2">
 | 
			
		||||
            <Button
 | 
			
		||||
              variant="outline"
 | 
			
		||||
@ -231,8 +307,8 @@ export default function DocumentManager() {
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <Card>
 | 
			
		||||
          <CardHeader>
 | 
			
		||||
        <Card className="flex-1 flex flex-col border rounded-md min-h-0 mb-0">
 | 
			
		||||
          <CardHeader className="flex-none py-2 px-4">
 | 
			
		||||
            <div className="flex justify-between items-center">
 | 
			
		||||
              <CardTitle>{t('documentPanel.documentManager.uploadedTitle')}</CardTitle>
 | 
			
		||||
              <div className="flex items-center gap-2">
 | 
			
		||||
@ -250,95 +326,101 @@ export default function DocumentManager() {
 | 
			
		||||
                </Button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <CardDescription>{t('documentPanel.documentManager.uploadedDescription')}</CardDescription>
 | 
			
		||||
            <CardDescription aria-hidden="true" className="hidden">{t('documentPanel.documentManager.uploadedDescription')}</CardDescription>
 | 
			
		||||
          </CardHeader>
 | 
			
		||||
 | 
			
		||||
          <CardContent>
 | 
			
		||||
          <CardContent className="flex-1 relative p-0" ref={cardContentRef}>
 | 
			
		||||
            {!docs && (
 | 
			
		||||
              <EmptyCard
 | 
			
		||||
                title={t('documentPanel.documentManager.emptyTitle')}
 | 
			
		||||
                description={t('documentPanel.documentManager.emptyDescription')}
 | 
			
		||||
              />
 | 
			
		||||
              <div className="absolute inset-0 p-0">
 | 
			
		||||
                <EmptyCard
 | 
			
		||||
                  title={t('documentPanel.documentManager.emptyTitle')}
 | 
			
		||||
                  description={t('documentPanel.documentManager.emptyDescription')}
 | 
			
		||||
                />
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
            {docs && (
 | 
			
		||||
              <Table>
 | 
			
		||||
                <TableHeader>
 | 
			
		||||
                  <TableRow>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.id')}</TableHead>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.summary')}</TableHead>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.status')}</TableHead>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.length')}</TableHead>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.chunks')}</TableHead>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.created')}</TableHead>
 | 
			
		||||
                    <TableHead>{t('documentPanel.documentManager.columns.updated')}</TableHead>
 | 
			
		||||
                  </TableRow>
 | 
			
		||||
                </TableHeader>
 | 
			
		||||
                <TableBody className="text-sm">
 | 
			
		||||
                  {Object.entries(docs.statuses).map(([status, documents]) =>
 | 
			
		||||
                    documents.map((doc) => (
 | 
			
		||||
                      <TableRow key={doc.id}>
 | 
			
		||||
                        <TableCell className="truncate font-mono overflow-visible">
 | 
			
		||||
                          {showFileName ? (
 | 
			
		||||
                            <>
 | 
			
		||||
                              <div className="group relative overflow-visible">
 | 
			
		||||
                                <div className="truncate">
 | 
			
		||||
                                  {getDisplayFileName(doc, 35)}
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[800px] 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">
 | 
			
		||||
                                  {doc.file_path}
 | 
			
		||||
                                </div>
 | 
			
		||||
                              </div>
 | 
			
		||||
                              <div className="text-xs text-gray-500">{doc.id}</div>
 | 
			
		||||
                            </>
 | 
			
		||||
                          ) : (
 | 
			
		||||
                            <div className="group relative overflow-visible">
 | 
			
		||||
                              <div className="truncate">
 | 
			
		||||
                                {doc.id}
 | 
			
		||||
                              </div>
 | 
			
		||||
                              <div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[800px] 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">
 | 
			
		||||
                                {doc.file_path}
 | 
			
		||||
                              </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                          )}
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                        <TableCell className="max-w-xs min-w-24 truncate overflow-visible">
 | 
			
		||||
                          <div className="group relative overflow-visible">
 | 
			
		||||
                            <div className="truncate">
 | 
			
		||||
                              {doc.content_summary}
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div className="invisible group-hover:visible absolute z-[9999] mt-1 max-w-[800px] 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">
 | 
			
		||||
                              {doc.content_summary}
 | 
			
		||||
                            </div>
 | 
			
		||||
                          </div>
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                        <TableCell>
 | 
			
		||||
                          {status === 'processed' && (
 | 
			
		||||
                            <span className="text-green-600">{t('documentPanel.documentManager.status.completed')}</span>
 | 
			
		||||
                          )}
 | 
			
		||||
                          {status === 'processing' && (
 | 
			
		||||
                            <span className="text-blue-600">{t('documentPanel.documentManager.status.processing')}</span>
 | 
			
		||||
                          )}
 | 
			
		||||
                          {status === 'pending' && <span className="text-yellow-600">{t('documentPanel.documentManager.status.pending')}</span>}
 | 
			
		||||
                          {status === 'failed' && <span className="text-red-600">{t('documentPanel.documentManager.status.failed')}</span>}
 | 
			
		||||
                          {doc.error && (
 | 
			
		||||
                            <span className="ml-2 text-red-500" title={doc.error}>
 | 
			
		||||
                              ⚠️
 | 
			
		||||
                            </span>
 | 
			
		||||
                          )}
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                        <TableCell>{doc.content_length ?? '-'}</TableCell>
 | 
			
		||||
                        <TableCell>{doc.chunks_count ?? '-'}</TableCell>
 | 
			
		||||
                        <TableCell className="truncate">
 | 
			
		||||
                          {new Date(doc.created_at).toLocaleString()}
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                        <TableCell className="truncate">
 | 
			
		||||
                          {new Date(doc.updated_at).toLocaleString()}
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
              <div className="absolute inset-0 flex flex-col p-0">
 | 
			
		||||
                <div className="w-full h-full flex flex-col rounded-lg border border-gray-200 dark:border-gray-700 overflow-auto">
 | 
			
		||||
                  <Table className="w-full">
 | 
			
		||||
                    <TableHeader className="sticky top-0 bg-background z-10 shadow-sm">
 | 
			
		||||
                      <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)]">
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.id')}</TableHead>
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.summary')}</TableHead>
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.status')}</TableHead>
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.length')}</TableHead>
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.chunks')}</TableHead>
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.created')}</TableHead>
 | 
			
		||||
                        <TableHead>{t('documentPanel.documentManager.columns.updated')}</TableHead>
 | 
			
		||||
                      </TableRow>
 | 
			
		||||
                    ))
 | 
			
		||||
                  )}
 | 
			
		||||
                </TableBody>
 | 
			
		||||
              </Table>
 | 
			
		||||
                    </TableHeader>
 | 
			
		||||
                    <TableBody className="text-sm overflow-auto">
 | 
			
		||||
                      {Object.entries(docs.statuses).map(([status, documents]) =>
 | 
			
		||||
                        documents.map((doc) => (
 | 
			
		||||
                          <TableRow key={doc.id}>
 | 
			
		||||
                            <TableCell className="truncate font-mono overflow-visible max-w-[250px]">
 | 
			
		||||
                              {showFileName ? (
 | 
			
		||||
                                <>
 | 
			
		||||
                                  <div className="group relative overflow-visible tooltip-container">
 | 
			
		||||
                                    <div className="truncate">
 | 
			
		||||
                                      {getDisplayFileName(doc, 30)}
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <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">
 | 
			
		||||
                                      {doc.file_path}
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                  </div>
 | 
			
		||||
                                  <div className="text-xs text-gray-500">{doc.id}</div>
 | 
			
		||||
                                </>
 | 
			
		||||
                              ) : (
 | 
			
		||||
                                <div className="group relative overflow-visible tooltip-container">
 | 
			
		||||
                                  <div className="truncate">
 | 
			
		||||
                                    {doc.id}
 | 
			
		||||
                                  </div>
 | 
			
		||||
                                  <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">
 | 
			
		||||
                                    {doc.file_path}
 | 
			
		||||
                                  </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                              )}
 | 
			
		||||
                            </TableCell>
 | 
			
		||||
                            <TableCell className="max-w-xs min-w-45 truncate overflow-visible">
 | 
			
		||||
                              <div className="group relative overflow-visible tooltip-container">
 | 
			
		||||
                                <div className="truncate">
 | 
			
		||||
                                  {doc.content_summary}
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <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">
 | 
			
		||||
                                  {doc.content_summary}
 | 
			
		||||
                                </div>
 | 
			
		||||
                              </div>
 | 
			
		||||
                            </TableCell>
 | 
			
		||||
                            <TableCell>
 | 
			
		||||
                              {status === 'processed' && (
 | 
			
		||||
                                <span className="text-green-600">{t('documentPanel.documentManager.status.completed')}</span>
 | 
			
		||||
                              )}
 | 
			
		||||
                              {status === 'processing' && (
 | 
			
		||||
                                <span className="text-blue-600">{t('documentPanel.documentManager.status.processing')}</span>
 | 
			
		||||
                              )}
 | 
			
		||||
                              {status === 'pending' && <span className="text-yellow-600">{t('documentPanel.documentManager.status.pending')}</span>}
 | 
			
		||||
                              {status === 'failed' && <span className="text-red-600">{t('documentPanel.documentManager.status.failed')}</span>}
 | 
			
		||||
                              {doc.error && (
 | 
			
		||||
                                <span className="ml-2 text-red-500" title={doc.error}>
 | 
			
		||||
                                  ⚠️
 | 
			
		||||
                                </span>
 | 
			
		||||
                              )}
 | 
			
		||||
                            </TableCell>
 | 
			
		||||
                            <TableCell>{doc.content_length ?? '-'}</TableCell>
 | 
			
		||||
                            <TableCell>{doc.chunks_count ?? '-'}</TableCell>
 | 
			
		||||
                            <TableCell className="truncate">
 | 
			
		||||
                              {new Date(doc.created_at).toLocaleString()}
 | 
			
		||||
                            </TableCell>
 | 
			
		||||
                            <TableCell className="truncate">
 | 
			
		||||
                              {new Date(doc.updated_at).toLocaleString()}
 | 
			
		||||
                            </TableCell>
 | 
			
		||||
                          </TableRow>
 | 
			
		||||
                        ))
 | 
			
		||||
                      )}
 | 
			
		||||
                    </TableBody>
 | 
			
		||||
                  </Table>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
          </CardContent>
 | 
			
		||||
        </Card>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user