wwf
2025-05-20 938c3e5a587ce950a94964ea509b9e7f8834dfae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { Fragment, useState } from 'react'
import type { FC } from 'react'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Tooltip from './tooltip'
import ProgressTooltip from './progress-tooltip'
import type { Resources } from './index'
import {
  PortalToFollowElem,
  PortalToFollowElemContent,
  PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import FileIcon from '@/app/components/base/file-icon'
import {
  Hash02,
  Target04,
} from '@/app/components/base/icons/src/vender/line/general'
import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
import {
  BezierCurve03,
  TypeSquare,
} from '@/app/components/base/icons/src/vender/line/editor'
 
type PopupProps = {
  data: Resources
  showHitInfo?: boolean
}
 
const Popup: FC<PopupProps> = ({
  data,
  showHitInfo = false,
}) => {
  const { t } = useTranslation()
  const [open, setOpen] = useState(false)
  const fileType = data.dataSourceType !== 'notion'
    ? (/\.([^.]*)$/g.exec(data.documentName)?.[1] || '')
    : 'notion'
 
  return (
    <PortalToFollowElem
      open={open}
      onOpenChange={setOpen}
      placement='top-start'
      offset={{
        mainAxis: 8,
        crossAxis: -2,
      }}
    >
      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
        <div className='flex h-7 max-w-[240px] items-center rounded-lg bg-components-button-secondary-bg px-2'>
          <FileIcon type={fileType} className='mr-1 h-4 w-4 shrink-0' />
          <div className='truncate text-xs text-text-tertiary'>{data.documentName}</div>
        </div>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 1000 }}>
        <div className='max-w-[360px] rounded-xl bg-background-section-burn shadow-lg'>
          <div className='px-4 pb-2 pt-3'>
            <div className='flex h-[18px] items-center'>
              <FileIcon type={fileType} className='mr-1 h-4 w-4 shrink-0' />
              <div className='system-xs-medium truncate text-text-tertiary'>{data.documentName}</div>
            </div>
          </div>
          <div className='max-h-[450px] overflow-y-auto rounded-lg bg-components-panel-bg px-4 py-0.5'>
            <div className='w-full'>
              {
                data.sources.map((source, index) => (
                  <Fragment key={index}>
                    <div className='group py-3'>
                      <div className='mb-2 flex items-center justify-between'>
                        <div className='flex h-5 items-center rounded-md border border-divider-subtle px-1.5'>
                          <Hash02 className='mr-0.5 h-3 w-3 text-text-quaternary' />
                          <div className='text-[11px] font-medium text-text-tertiary'>
                            {source.segment_position || index + 1}
                          </div>
                        </div>
                        {
                          showHitInfo && (
                            <Link
                              href={`/datasets/${source.dataset_id}/documents/${source.document_id}`}
                              className='hidden h-[18px] items-center text-xs text-text-accent group-hover:flex'>
                              {t('common.chat.citation.linkToDataset')}
                              <ArrowUpRight className='ml-1 h-3 w-3' />
                            </Link>
                          )
                        }
                      </div>
                      <div className='break-words text-[13px] text-text-secondary'>{source.content}</div>
                      {
                        showHitInfo && (
                          <div className='system-xs-medium mt-2 flex flex-wrap items-center text-text-quaternary'>
                            <Tooltip
                              text={t('common.chat.citation.characters')}
                              data={source.word_count}
                              icon={<TypeSquare className='mr-1 h-3 w-3' />}
                            />
                            <Tooltip
                              text={t('common.chat.citation.hitCount')}
                              data={source.hit_count}
                              icon={<Target04 className='mr-1 h-3 w-3' />}
                            />
                            <Tooltip
                              text={t('common.chat.citation.vectorHash')}
                              data={source.index_node_hash?.substring(0, 7)}
                              icon={<BezierCurve03 className='mr-1 h-3 w-3' />}
                            />
                            {
                              source.score && (
                                <ProgressTooltip data={Number(source.score.toFixed(2))} />
                              )
                            }
                          </div>
                        )
                      }
                    </div>
                    {
                      index !== data.sources.length - 1 && (
                        <div className='my-1 h-[1px] bg-divider-regular' />
                      )
                    }
                  </Fragment>
                ))
              }
            </div>
          </div>
        </div>
      </PortalToFollowElemContent>
    </PortalToFollowElem>
  )
}
 
export default Popup