@ -1,10 +1,11 @@
import React , { type FC , useCallback , useMemo , useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Ri ArrowRightUpLine, Ri DeleteBinLine, RiEditLine } from '@remixicon/react'
import { Ri DeleteBinLine, RiEditLine } from '@remixicon/react'
import { StatusItem } from '../../list'
import DocumentFileIcon from '../../../common/document-file-icon'
import { useDocumentContext } from '../index'
import ChildSegmentList from './child-segment-list'
import Tag from './common/tag'
import Dot from './common/dot'
import { SegmentIndexTag , useSegmentListContext } from '.'
import type { SegmentDetailModel } from '@/models/datasets'
import Indicator from '@/app/components/header/indicator'
@ -17,74 +18,13 @@ import Badge from '@/app/components/base/badge'
import { isAfter } from '@/utils/time'
import Tooltip from '@/app/components/base/tooltip'
const Dot = React . memo ( ( ) = > {
return (
< div className = 'text-text-quaternary text-xs font-medium' > · < / div >
)
} )
Dot . displayName = 'Dot'
const ProgressBar : FC < { percent : number ; loading : boolean } > = React . memo ( ( { percent , loading } ) = > {
return (
< div className = '' >
< div className = '' >
< div
className = ''
style = { { width : ` ${ loading ? 0 : ( Math . min ( percent , 1 ) * 100 ) . toFixed ( 2 ) } % ` } }
/ >
< / div >
< div className = '' > { loading ? null : percent . toFixed ( 2 ) } < / div >
< / div >
)
} )
ProgressBar . displayName = 'ProgressBar'
type DocumentTitleProps = {
name : string
extension? : string
}
const DocumentTitle : FC < DocumentTitleProps > = React . memo ( ( { extension , name } ) = > {
return (
< div className = '' >
< DocumentFileIcon name = { name } extension = { extension } size = { 'sm' } / >
< span className = '' > { name || '--' } < / span >
< / div >
)
} )
DocumentTitle . displayName = 'DocumentTitle'
const Tag = React . memo ( ( { text } : { text : string } ) = > {
return (
< div className = 'inline-flex items-center gap-x-0.5' >
< span className = 'text-text-quaternary text-xs font-medium' > # < / span >
< span className = 'text-text-tertiary text-xs' > { text } < / span >
< / div >
)
} )
Tag . displayName = 'Tag'
export type UsageScene = 'doc' | 'hitTesting'
type ISegmentCardProps = {
loading : boolean
detail? : SegmentDetailModel & { document ? : { name : string } }
contentExternal? : string
refSource ? : {
title : string
uri : string
}
isExternal? : boolean
score? : number
onClick ? : ( ) = > void
onChangeSwitch ? : ( enabled : boolean , segId? : string ) = > Promise < void >
onDelete ? : ( segId : string ) = > Promise < void >
onClickEdit ? : ( ) = > void
scene? : UsageScene
className? : string
archived? : boolean
embeddingAvailable? : boolean
@ -92,16 +32,11 @@ type ISegmentCardProps = {
const SegmentCard : FC < ISegmentCardProps > = ( {
detail = { } ,
contentExternal ,
isExternal ,
refSource ,
score ,
onClick ,
onChangeSwitch ,
onDelete ,
onClickEdit ,
loading = true ,
scene = 'doc' ,
className = '' ,
archived ,
embeddingAvailable ,
@ -124,10 +59,6 @@ const SegmentCard: FC<ISegmentCardProps> = ({
const isCollapsed = useSegmentListContext ( s = > s . isCollapsed )
const [ mode , parentMode ] = useDocumentContext ( s = > [ s . mode , s . parentMode ] )
const isDocScene = useMemo ( ( ) = > {
return scene === 'doc'
} , [ scene ] )
const isGeneralMode = useMemo ( ( ) = > {
return mode === 'custom'
} , [ mode ] )
@ -147,9 +78,9 @@ const SegmentCard: FC<ISegmentCardProps> = ({
} , [ enabled ] )
const handleClickCard = useCallback ( ( ) = > {
if ( ! isFullDocMode )
if ( mode !== 'hierarchical' || parentMode !== 'full-doc' )
onClick ? . ( )
} , [ isFullDoc Mode, onClick ] )
} , [ mode, parent Mode, onClick ] )
const renderContent = ( ) = > {
if ( answer ) {
@ -166,10 +97,6 @@ const SegmentCard: FC<ISegmentCardProps> = ({
< / >
)
}
if ( contentExternal )
return contentExternal
return content
}
@ -179,96 +106,85 @@ const SegmentCard: FC<ISegmentCardProps> = ({
onClick = { handleClickCard }
>
< div className = 'h-5 relative flex items-center justify-between' >
{ isDocScene
? < >
< div className = 'flex items-center gap-x-2' >
< SegmentIndexTag positionId = { position } className = { textOpacity } / >
< Dot / >
< div className = { cn ( 'text-text-tertiary system-xs-medium' , textOpacity ) } > { ` ${ formatNumber ( word_count ) } Characters ` } < / div >
< Dot / >
< div className = { cn ( 'text-text-tertiary system-xs-medium' , textOpacity ) } > { ` ${ formatNumber ( hit_count ) } Retrieval Count ` } < / div >
{ chunkEdited && (
< >
< Dot / >
< Badge text = 'edited' uppercase className = { textOpacity } / >
< / >
) }
< / div >
{ ! isFullDocMode
? < div className = 'flex items-center' >
{ loading
? (
< Indicator color = "gray" / >
)
: (
< >
< StatusItem status = { enabled ? 'enabled' : 'disabled' } reverse textCls = "text-text-tertiary system-xs-regular" / >
{ embeddingAvailable && (
< div className = " absolute - top - 2 - right - 2.5 z - 20 hidden group - hover / card : flex items - center gap - x - 0.5 p - 1
rounded - [ 10 px ] border - [ 0.5 px ] border - components - actionbar - border bg - components - actionbar - bg shadow - md backdrop - blur - [ 5 px ] " >
{ ! archived && (
< >
< Tooltip
popupContent = 'Edit'
popupClassName = 'text-text-secondary system-xs-medium'
>
< div
className = 'shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-base-hover cursor-pointer'
onClick = { ( e ) = > {
e . stopPropagation ( )
onClickEdit ? . ( )
} } >
< RiEditLine className = 'w-4 h-4 text-text-tertiary' / >
< / div >
< / Tooltip >
< Tooltip
popupContent = 'Delete'
popupClassName = 'text-text-secondary system-xs-medium'
>
< div className = 'shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-destructive-hover cursor-pointer group/delete'
onClick = { ( e ) = > {
e . stopPropagation ( )
setShowModal ( true )
}
} >
< RiDeleteBinLine className = 'w-4 h-4 text-text-tertiary group-hover/delete:text-text-destructive' / >
< / div >
< / Tooltip >
< Divider type = "vertical" className = "h-3.5 bg-divider-regular" / >
< / >
) }
< div
onClick = { ( e : React.MouseEvent < HTMLDivElement , MouseEvent > ) = >
e . stopPropagation ( )
}
className = "flex items-center"
>
< Switch
size = 'md'
disabled = { archived || detail . status !== 'completed' }
defaultValue = { enabled }
onChange = { async ( val ) = > {
await onChangeSwitch ? . ( val , id )
} }
/ >
< / div >
< >
< div className = 'flex items-center gap-x-2' >
< SegmentIndexTag positionId = { position } className = { textOpacity } / >
< Dot / >
< div className = { cn ( 'text-text-tertiary system-xs-medium' , textOpacity ) } > { ` ${ formatNumber ( word_count ) } Characters ` } < / div >
< Dot / >
< div className = { cn ( 'text-text-tertiary system-xs-medium' , textOpacity ) } > { ` ${ formatNumber ( hit_count ) } Retrieval Count ` } < / div >
{ chunkEdited && (
< >
< Dot / >
< Badge text = 'edited' uppercase className = { textOpacity } / >
< / >
) }
< / div >
{ ! isFullDocMode
? < div className = 'flex items-center' >
{ loading
? (
< Indicator color = "gray" / >
)
: (
< >
< StatusItem status = { enabled ? 'enabled' : 'disabled' } reverse textCls = "text-text-tertiary system-xs-regular" / >
{ embeddingAvailable && (
< div className = " absolute - top - 2 - right - 2.5 z - 20 hidden group - hover / card : flex items - center gap - x - 0.5 p - 1
rounded - [ 10 px ] border - [ 0.5 px ] border - components - actionbar - border bg - components - actionbar - bg shadow - md backdrop - blur - [ 5 px ] " >
{ ! archived && (
< >
< Tooltip
popupContent = 'Edit'
popupClassName = 'text-text-secondary system-xs-medium'
>
< div
className = 'shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-base-hover cursor-pointer'
onClick = { ( e ) = > {
e . stopPropagation ( )
onClickEdit ? . ( )
} } >
< RiEditLine className = 'w-4 h-4 text-text-tertiary' / >
< / div >
< / Tooltip >
< Tooltip
popupContent = 'Delete'
popupClassName = 'text-text-secondary system-xs-medium'
>
< div className = 'shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-destructive-hover cursor-pointer group/delete'
onClick = { ( e ) = > {
e . stopPropagation ( )
setShowModal ( true )
}
} >
< RiDeleteBinLine className = 'w-4 h-4 text-text-tertiary group-hover/delete:text-text-destructive' / >
< / div >
< / Tooltip >
< Divider type = "vertical" className = "h-3.5 bg-divider-regular" / >
< / >
) }
< div
onClick = { ( e : React.MouseEvent < HTMLDivElement , MouseEvent > ) = >
e . stopPropagation ( )
}
className = "flex items-center"
>
< Switch
size = 'md'
disabled = { archived || detail . status !== 'completed' }
defaultValue = { enabled }
onChange = { async ( val ) = > {
await onChangeSwitch ? . ( val , id )
} }
/ >
< / div >
) }
< / >
) }
< / div >
: null }
< / >
: (
score !== null
? (
< div className = '' >
< div className = '' / >
< ProgressBar percent = { score ? ? 0 } loading = { loading } / >
< / div >
)
: null
) }
< / div >
) }
< / >
) }
< / div >
: null }
< / >
< / div >
{ loading
? (
@ -277,50 +193,32 @@ const SegmentCard: FC<ISegmentCardProps> = ({
< / div >
)
: (
isDocScene
? < >
< div className = { cn ( 'text-text-secondary body-md-regular -tracking-[0.07px] mt-0.5' ,
textOpacity ,
isCollapsed ? 'line-clamp-2' : 'line-clamp-20' ,
) } >
{ renderContent ( ) }
< / div >
{ isGeneralMode && < div className = { cn ( 'flex items-center gap-x-2 py-1.5' , textOpacity ) } >
{ keywords ? . map ( keyword = > < Tag key = { keyword } text = { keyword } / > ) }
< / div > }
{
isFullDocMode
? < button className = 'mt-0.5 mb-2 text-text-accent system-xs-semibold-uppercase' onClick = { ( ) = > onClick ? . ( ) } > VIEW MORE < / button >
: null
}
{
child_chunks . length > 0
&& < ChildSegmentList
childChunks = { child_chunks }
handleInputChange = { ( ) = > { } }
enabled = { enabled }
/ >
}
< / >
: < >
< div className = 'text-text-secondary body-md-regular -tracking-[0.07px]' >
{ renderContent ( ) }
< / div >
< div className = '' >
< Divider / >
< div className = "relative flex items-center w-full pb-1" >
< DocumentTitle
name = { detail ? . document ? . name || refSource ? . title || '' }
extension = { ( detail ? . document ? . name || refSource ? . title || '' ) . split ( '.' ) . pop ( ) || 'txt' }
/ >
< div className = '' >
{ isExternal ? t ( 'datasetHitTesting.viewDetail' ) : t ( 'datasetHitTesting.viewChart' ) }
< RiArrowRightUpLine className = "w-3.5 h-3.5 ml-1" / >
< / div >
< / div >
< / div >
< / >
) }
< >
< div className = { cn ( 'text-text-secondary body-md-regular -tracking-[0.07px] mt-0.5' ,
textOpacity ,
isCollapsed ? 'line-clamp-2' : 'line-clamp-20' ,
) } >
{ renderContent ( ) }
< / div >
{ isGeneralMode && < div className = { cn ( 'flex items-center gap-x-2 py-1.5' , textOpacity ) } >
{ keywords ? . map ( keyword = > < Tag key = { keyword } text = { keyword } / > ) }
< / div > }
{
isFullDocMode
? < button className = 'mt-0.5 mb-2 text-text-accent system-xs-semibold-uppercase' onClick = { ( ) = > onClick ? . ( ) } > VIEW MORE < / button >
: null
}
{
child_chunks . length > 0
&& < ChildSegmentList
childChunks = { child_chunks }
handleInputChange = { ( ) = > { } }
enabled = { enabled }
/ >
}
< / >
)
}
{ showModal
&& < Confirm
isShow = { showModal }