mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-09-07 17:58:55 +08:00
refactor(ui): update knowledge graph, chunk, metadata, agent log styles (#13518)
### What problem does this PR solve? Update UI styles: - **Dataset** > **Knowledge graph** tooltip - **Dataset** > **Files** > **Manage metadata** modal - **Dataset** > **Files** > **Modify Chunking Method** > **Auto metadata** > **Manage generation settings** modal - **Agent** > **Canvas (Ingestion pipeline)** > **Dataflow result** ### Type of change - [x] Refactoring
This commit is contained in:
@@ -4,7 +4,7 @@ import { DatePicker } from '@/components/ui/date-picker';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { ColumnDef, Row, Table } from '@tanstack/react-table';
|
||||
import { ListChevronsDownUp, Settings, Trash2 } from 'lucide-react';
|
||||
import { ListChevronsDownUp, LucidePencil, Trash2 } from 'lucide-react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -147,7 +147,7 @@ export const useMetadataColumns = ({
|
||||
header: () => <span>{t('knowledgeDetails.metadata.description')}</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-sm truncate max-w-32">
|
||||
{row.getValue('description')}
|
||||
{row.getValue('description') || '-'}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -347,17 +347,17 @@ export const useMetadataColumns = ({
|
||||
cell: ({ row }) => (
|
||||
<div className=" flex opacity-0 group-hover:opacity-100 gap-2">
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="bg-transparent px-1 py-0"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={() => {
|
||||
handleEditValueRow(row.original, row.index);
|
||||
}}
|
||||
>
|
||||
<Settings />
|
||||
<LucidePencil />
|
||||
</Button>
|
||||
<Button
|
||||
variant={'delete'}
|
||||
className="p-0 bg-transparent"
|
||||
variant="delete"
|
||||
size="icon-sm"
|
||||
onClick={() => {
|
||||
setDeleteDialogContent({
|
||||
visible: true,
|
||||
|
||||
@@ -321,36 +321,8 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
>
|
||||
<>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="w-1/2">
|
||||
{secondTitle || t('knowledgeDetails.metadata.metadata')}
|
||||
</div>
|
||||
<div>
|
||||
{/* {metadataType === MetadataType.Manage && (
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="border border-border-button"
|
||||
type="button"
|
||||
onClick={handleMenuClick(Routes.DataSetSetting, {
|
||||
openMetadata: true,
|
||||
})}
|
||||
>
|
||||
{t('knowledgeDetails.metadata.toMetadataSetting')}
|
||||
</Button>
|
||||
)} */}
|
||||
{isCanAdd && activeTab !== 'built-in' && (
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="border border-border-button"
|
||||
type="button"
|
||||
onClick={handAddValueRow}
|
||||
data-testid={addButtonTestId}
|
||||
>
|
||||
<Plus />
|
||||
{t('common.add')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-4 items-center justify-between text-truncate">
|
||||
{secondTitle || t('knowledgeDetails.metadata.metadata')}
|
||||
</div>
|
||||
|
||||
{rowSelectionIsEmpty || (
|
||||
@@ -366,14 +338,43 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
|
||||
value={activeTab}
|
||||
onValueChange={(v) => setActiveTab(v as MetadataSettingsTab)}
|
||||
>
|
||||
<TabsList className="w-fit">
|
||||
<TabsTrigger value="generation">
|
||||
{t('knowledgeDetails.metadata.generation')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="built-in">
|
||||
{t('knowledgeDetails.metadata.builtIn')}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="flex gap-4 items-center justify-between">
|
||||
<TabsList className="w-fit">
|
||||
<TabsTrigger value="generation">
|
||||
{t('knowledgeDetails.metadata.generation')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="built-in">
|
||||
{t('knowledgeDetails.metadata.builtIn')}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div>
|
||||
{/* {metadataType === MetadataType.Manage && (
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="border border-border-button"
|
||||
type="button"
|
||||
onClick={handleMenuClick(Routes.DataSetSetting, {
|
||||
openMetadata: true,
|
||||
})}
|
||||
>
|
||||
{t('knowledgeDetails.metadata.toMetadataSetting')}
|
||||
</Button>
|
||||
)} */}
|
||||
{isCanAdd && activeTab !== 'built-in' && (
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={handAddValueRow}
|
||||
data-testid={addButtonTestId}
|
||||
>
|
||||
<Plus />
|
||||
{t('common.add')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="generation">
|
||||
<Table rootClassName="max-h-[800px]">
|
||||
<TableHeader>
|
||||
|
||||
@@ -283,8 +283,8 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
|
||||
metaData.valueType === metadataValueTypeEnum['list'] && (
|
||||
<div>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className="border border-border-button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleAddValue}
|
||||
data-testid={addValueButtonTestId}
|
||||
>
|
||||
|
||||
@@ -31,13 +31,14 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||
}, [chunkMethod]);
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div>
|
||||
{imageList.length > 0 ? (
|
||||
<>
|
||||
<h5 className="font-semibold text-base mt-0 mb-1">
|
||||
{`"${item.title}" ${t('methodTitle')}`}
|
||||
</h5>
|
||||
<p
|
||||
className="[&_ul]:list-disc [&_ol]:list-decimal [&_:is(ul,ol)]:pl-8"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(item.description),
|
||||
}}
|
||||
@@ -68,7 +69,7 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||
</div>
|
||||
)}
|
||||
{chunkMethod === 'tag' && <TagTabs></TagTabs>}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { t } from 'i18next';
|
||||
import { X } from 'lucide-react';
|
||||
import { LucideX } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import CategoryPanel from './category-panel';
|
||||
|
||||
@@ -20,20 +21,25 @@ const ChunkMethodLearnMore = ({ parserId }: { parserId: string }) => {
|
||||
{t('knowledgeDetails.learnMore')}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className="bg-[#FFF]/10 p-[20px] rounded-[12px] mt-[10px] relative flex-1 overflow-auto"
|
||||
|
||||
<Card
|
||||
as="article"
|
||||
className="relative flex-1 overflow-auto mt-4"
|
||||
style={{ display: visible ? 'block' : 'none' }}
|
||||
>
|
||||
<CategoryPanel chunkMethod={parserId}></CategoryPanel>
|
||||
<div
|
||||
className="absolute right-1 top-1 cursor-pointer hover:text-[#FFF]/30"
|
||||
onClick={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
<Button
|
||||
className="absolute right-2 top-2"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={() => setVisible(false)}
|
||||
>
|
||||
<X />
|
||||
</div>
|
||||
</div>
|
||||
<LucideX />
|
||||
</Button>
|
||||
|
||||
<CardContent className="p-5">
|
||||
<CategoryPanel chunkMethod={parserId}></CategoryPanel>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -415,8 +415,8 @@ export function AutoMetadata({
|
||||
avatar={knowledgeBase.avatar}
|
||||
name={knowledgeBase.name}
|
||||
className="size-8"
|
||||
></RAGFlowAvatar>
|
||||
<div className=" text-text-primary text-base space-y-1 overflow-hidden">
|
||||
/>
|
||||
<div className="text-text-primary text-base space-y-1 truncate overflow-hidden">
|
||||
{knowledgeBase.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,6 @@ export function useDatasetTableColumns({
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
className="size-3"
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
@@ -57,7 +56,6 @@ export function useDatasetTableColumns({
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
className="size-3"
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { ElementDatum, Graph, IElementEvent } from '@antv/g6';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useCallback, useEffect, useId, useMemo, useRef } from 'react';
|
||||
import { buildNodesAndCombos, defaultComboLabel } from './util';
|
||||
|
||||
import { useIsDarkTheme } from '@/components/theme-provider';
|
||||
import { cn } from '@/lib/utils';
|
||||
import styles from './index.module.less';
|
||||
|
||||
const TooltipColorMap = {
|
||||
combo: 'red',
|
||||
node: 'black',
|
||||
edge: 'blue',
|
||||
combo: 'text-red-600',
|
||||
node: 'text-black',
|
||||
edge: 'text-blue-600',
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
@@ -18,6 +19,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const ForceGraph = ({ data, show }: IProps) => {
|
||||
const tooltipId = useId();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const graphRef = useRef<Graph | null>(null);
|
||||
const isDark = useIsDarkTheme();
|
||||
@@ -52,23 +54,46 @@ const ForceGraph = ({ data, show }: IProps) => {
|
||||
getContent: (e: IElementEvent, items: ElementDatum) => {
|
||||
if (Array.isArray(items)) {
|
||||
if (items.some((x) => x?.isCombo)) {
|
||||
return `<p style="font-weight:600;color:red">${items?.[0]?.data?.label}</p>`;
|
||||
return `<p class="font-semibold text-red-600">${items?.[0]?.data?.label}</p>`;
|
||||
}
|
||||
let result = ``;
|
||||
items.forEach((item) => {
|
||||
result += `<section style="color:${TooltipColorMap[e['targetType'] as keyof typeof TooltipColorMap]};"><h3>${item?.id}</h3>`;
|
||||
if (item?.entity_type) {
|
||||
result += `<div style="padding-bottom: 6px;"><b>Entity type: </b>${item?.entity_type}</div>`;
|
||||
}
|
||||
if (item?.weight) {
|
||||
result += `<div><b>Weight: </b>${item?.weight}</div>`;
|
||||
}
|
||||
if (item?.description) {
|
||||
result += `<p>${item?.description}</p>`;
|
||||
}
|
||||
});
|
||||
return result + '</section>';
|
||||
|
||||
return items
|
||||
.flatMap((item) => {
|
||||
return [
|
||||
'<div ',
|
||||
`id="${tooltipId}"`,
|
||||
`aria-label="${item?.id}"`,
|
||||
`role="tooltip"`,
|
||||
`class="${TooltipColorMap[e['targetType'] as keyof typeof TooltipColorMap]}"`,
|
||||
'>',
|
||||
`<h3>${item?.id}</h3>`,
|
||||
'<dl class="mb-1 empty:hidden">',
|
||||
...(item?.entity_type
|
||||
? [
|
||||
'<div class="flex items-center gap-[.5ch]">',
|
||||
'<dt><b>Entity type: </b></dt>',
|
||||
`<dd>${item.entity_type}</dd>`,
|
||||
'</div>',
|
||||
]
|
||||
: []),
|
||||
...(item?.weight
|
||||
? [
|
||||
'<div class="flex items-center gap-[.5ch]">',
|
||||
'<dt><b>Weight: </b></dt>',
|
||||
`<dd>${item.weight}</dd>`,
|
||||
'</div>',
|
||||
]
|
||||
: []),
|
||||
'</dl>',
|
||||
item.description
|
||||
? `<p class="text-xs">${item.description}</p>`
|
||||
: '',
|
||||
'</div>',
|
||||
];
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
@@ -82,34 +107,32 @@ const ForceGraph = ({ data, show }: IProps) => {
|
||||
node: {
|
||||
style: {
|
||||
size: (d) => {
|
||||
let size = 100 + ((d.rank as number) || 0) * 5;
|
||||
size = size > 300 ? 300 : size;
|
||||
return size;
|
||||
const size = 100 + ((d.rank as number) || 0) * 5;
|
||||
return Math.min(size, 300);
|
||||
},
|
||||
|
||||
labelText: (d) => d.id,
|
||||
labelFill: isDark ? 'rgba(255,255,255,1)' : 'rgba(0,0,0,1)',
|
||||
// labelPadding: 30,
|
||||
labelFontSize: 40,
|
||||
// labelOffsetX: 20,
|
||||
// labelOffsetX: 20,
|
||||
labelOffsetY: 20,
|
||||
labelPlacement: 'center',
|
||||
labelWordWrap: true,
|
||||
},
|
||||
palette: {
|
||||
type: 'group',
|
||||
field: (d) => {
|
||||
return d?.entity_type as string;
|
||||
},
|
||||
field: (d) => d?.entity_type as string,
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
style: (model) => {
|
||||
const weight: number = Number(model?.weight) || 2;
|
||||
const lineWeight = weight * 4;
|
||||
|
||||
return {
|
||||
stroke: isDark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)',
|
||||
lineDash: [10, 10],
|
||||
lineWidth: lineWeight > 8 ? 8 : lineWeight,
|
||||
lineWidth: Math.min(weight * 4, 8),
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -149,12 +172,9 @@ const ForceGraph = ({ data, show }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={styles.forceContainer}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: show ? 'block' : 'none',
|
||||
}}
|
||||
className={cn(styles.forceContainer, 'size-full', !show && 'hidden')}
|
||||
aria-haspopup="true"
|
||||
aria-describedby={tooltipId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.forceContainer {
|
||||
:global(.tooltip) {
|
||||
border-radius: 10px !important;
|
||||
padding: 0.5rem 0.75rem !important;
|
||||
border-radius: 0.5rem !important;
|
||||
font-family: var(--font-sans) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { useFetchKnowledgeGraph } from '@/hooks/use-knowledge-request';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { LucideTrash2 } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ForceGraph from './force-graph';
|
||||
@@ -13,18 +14,23 @@ const KnowledgeGraph: React.FC = () => {
|
||||
const { handleDeleteKnowledgeGraph } = useDeleteKnowledgeGraph();
|
||||
|
||||
return (
|
||||
<section className={'w-full h-[90dvh] relative p-6'}>
|
||||
<Card
|
||||
as="article"
|
||||
className="relative me-5 mb-5 p-0 bg-transparent shadow-none overflow-hidden"
|
||||
>
|
||||
<ConfirmDeleteDialog onOk={handleDeleteKnowledgeGraph}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size={'sm'}
|
||||
className="absolute right-0 top-0 z-50"
|
||||
size="sm"
|
||||
className="absolute right-5 top-5 z-50"
|
||||
>
|
||||
<Trash2 /> {t('common.delete')}
|
||||
<LucideTrash2 />
|
||||
{t('common.delete')}
|
||||
</Button>
|
||||
</ConfirmDeleteDialog>
|
||||
<ForceGraph data={data?.graph} show></ForceGraph>
|
||||
</section>
|
||||
|
||||
<ForceGraph data={data?.graph} show />
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user