Compare commits

...

23 Commits

Author SHA1 Message Date
AkaraChen
4cea0248c8 chore: upd 2024-11-26 16:05:58 +08:00
AkaraChen
e26a963163 fix: missing translation 2024-11-26 16:05:14 +08:00
AkaraChen
72ca69fb2a Merge branch 'feat/parent-child-retrieval' of https://github.com/langgenius/dify into feat/parent-child-retrieval 2024-11-26 15:38:03 +08:00
AkaraChen
db1956ed54 feat: dataset creation form reset 2024-11-26 15:37:57 +08:00
Joel
9aceceda2c fix: css var value 2024-11-26 15:33:58 +08:00
AkaraChen
1843f6ccb6 refactor: input number component 2024-11-26 15:29:31 +08:00
AkaraChen
b7d9987953 chore: align design token 2024-11-26 14:48:49 +08:00
AkaraChen
b528b1e875 refactor: use css var 2024-11-26 14:33:40 +08:00
AkaraChen
c4aa98e609 Merge branch 'main' into feat/parent-child-retrieval 2024-11-26 14:18:56 +08:00
AkaraChen
4757db1b6b refactor: use css var 2024-11-26 14:18:42 +08:00
AkaraChen
1a6a28f650 feat: settings ui for database pre-preprocessing 2024-11-25 17:57:31 +08:00
AkaraChen
bba9301788 fix: layout overflow 2024-11-22 14:09:41 +08:00
AkaraChen
7881fb4d22 fix: layout overflow 2024-11-22 13:12:24 +08:00
AkaraChen
fdcee1cd45 feat: create top bar 2024-11-21 16:19:32 +08:00
twwu
13c62f83f4 Merge branch 'feat/parent-child-retrieval' of https://github.com/langgenius/dify into feat/parent-child-retrieval 2024-11-21 15:13:21 +08:00
twwu
c5b9a829c0 feat: update UI styles and enhance status indicator components 2024-11-21 15:13:16 +08:00
AkaraChen
6f3a1c9d72 feat: parent child state 2024-11-21 11:40:17 +08:00
twwu
13bb4aa721 Merge branch 'feat/parent-child-retrieval' of https://github.com/langgenius/dify into feat/parent-child-retrieval 2024-11-20 16:38:48 +08:00
twwu
d0ef423e66 feat: enhance SegmentCard and SegmentAdd components with new DocumentTitle and improved UI elements 2024-11-20 16:38:38 +08:00
AkaraChen
8e1aef6120 feat: new dataset footer ui 2024-11-20 16:24:06 +08:00
AkaraChen
4657df17a9 revert: qa mode 2024-11-20 15:25:26 +08:00
AkaraChen
27ece2fb52 feat: most ui for create datasets
chore: upd
2024-11-20 14:55:59 +08:00
AkaraChen
ca4d0fb4cc feat: option card component
chore: upd
2024-11-20 10:13:40 +08:00
49 changed files with 1272 additions and 524 deletions

View File

@@ -0,0 +1,55 @@
import type { FC, SetStateAction } from 'react'
import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react'
import Input, { type InputProps } from '../input'
import classNames from '@/utils/classnames'
export type InputNumberProps = {
unit?: string
value: number
onChange: (value: number) => void
amount?: number
size?: 'sm' | 'md'
} & Omit<InputProps, 'value' | 'onChange' | 'size'>
export const InputNumber: FC<InputNumberProps> = (props) => {
const { unit, className, onChange, amount = 1, value, size = 'sm', max, min, ...rest } = props
const update = (input: SetStateAction<number>) => {
const current = typeof input === 'function' ? input(value) : input as number
if (max && current >= (max as number))
return
if (min && current <= (min as number))
return
onChange(current)
}
const inc = () => update(val => val + amount)
const dec = () => update(val => val - amount)
return <div className='flex'>
<Input {...rest}
className={classNames('rounded-r-none', className)}
value={value}
max={max}
min={min}
onChange={(e) => {
const parsed = Number(e.target.value)
if (Number.isNaN(parsed))
return
onChange(parsed)
}}
/>
{unit && <div className='flex items-center bg-components-input-bg-normal text-[13px] text-text-placeholder pr-2'>{unit}</div>}
<div className='flex flex-col bg-components-input-bg-normal rounded-r-md border-l text-text-tertiary'>
<button onClick={inc} className={classNames(
size === 'sm' ? 'pt-1' : 'pt-1.5',
'px-1.5 hover:bg-components-input-bg-hover',
)}>
<RiArrowUpSLine className='size-3' />
</button>
<button onClick={dec} className={classNames(
size === 'sm' ? 'pb-1' : 'pb-1.5',
'px-1.5 hover:bg-components-input-bg-hover',
)}>
<RiArrowDownSLine className='size-3' />
</button>
</div>
</div>
}

View File

@@ -1,5 +1,6 @@
'use client'
import type { FC } from 'react'
import { InputNumber } from '../input-number'
import Tooltip from '@/app/components/base/tooltip'
import Slider from '@/app/components/base/slider'
import Switch from '@/app/components/base/switch'
@@ -47,13 +48,18 @@ const ParamItem: FC<Props> = ({ className, id, name, noTooltip, tip, step = 0.1,
</div>
<div className="mt-2 flex items-center">
<div className="mr-4 flex shrink-0 items-center">
<input disabled={!enable} type="number" min={min} max={max} step={step} className="block w-[48px] h-7 text-xs leading-[18px] rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600 disabled:opacity-60" value={(value === null || value === undefined) ? '' : value} onChange={(e) => {
const value = parseFloat(e.target.value)
if (value < min || value > max)
return
onChange(id, value)
}} />
<InputNumber
disabled={!enable}
type="number"
min={min}
max={max}
step={step}
size='sm'
value={value}
onChange={(value) => {
onChange(id, value)
}}
/>
</div>
<div className="flex items-center h-7 grow">
<Slider

View File

@@ -26,12 +26,14 @@ const RadioCard: FC<Props> = ({
onChosen = () => { },
chosenConfig,
chosenConfigWrapClassName,
className,
}) => {
return (
<div
className={cn(
'border border-components-option-card-option-border bg-components-option-card-option-bg rounded-xl hover:shadow-xs cursor-pointer',
isChosen && 'bg-components-option-card-option-selected-bg border-components-panel-border shadow-xs',
className,
)}
>
<div className='flex py-3 pl-3 pr-4' onClick={onChosen}>

View File

@@ -2,10 +2,11 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Image from 'next/image'
import RetrievalParamConfig from '../retrieval-param-config'
import { OptionCard } from '../../create/step-two/option-card'
import { retrievalIcon } from '../../create/icons'
import { RETRIEVE_METHOD } from '@/types/app'
import RadioCard from '@/app/components/base/radio-card'
import { HighPriority } from '@/app/components/base/icons/src/vender/solid/arrows'
import type { RetrievalConfig } from '@/types/app'
type Props = {
@@ -21,19 +22,17 @@ const EconomicalRetrievalMethodConfig: FC<Props> = ({
return (
<div className='space-y-2'>
<RadioCard
icon={<HighPriority className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.vector} alt='' />}
title={t('dataset.retrieval.invertedIndex.title')}
description={t('dataset.retrieval.invertedIndex.description')}
noRadio
chosenConfig={
<RetrievalParamConfig
type={RETRIEVE_METHOD.invertedIndex}
value={value}
onChange={onChange}
/>
}
/>
description={t('dataset.retrieval.invertedIndex.description')} isActive
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.invertedIndex}
value={value}
onChange={onChange}
/>
</OptionCard>
</div>
)
}

View File

@@ -2,12 +2,13 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Image from 'next/image'
import RetrievalParamConfig from '../retrieval-param-config'
import { OptionCard } from '../../create/step-two/option-card'
import Effect from '../../create/assets/option-card-effect-purple.svg'
import { retrievalIcon } from '../../create/icons'
import type { RetrievalConfig } from '@/types/app'
import { RETRIEVE_METHOD } from '@/types/app'
import RadioCard from '@/app/components/base/radio-card'
import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development'
import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
import { useProviderContext } from '@/context/provider-context'
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
@@ -16,6 +17,7 @@ import {
RerankingModeEnum,
WeightedScoreEnum,
} from '@/models/datasets'
import Badge from '@/app/components/base/badge'
type Props = {
value: RetrievalConfig
@@ -56,67 +58,72 @@ const RetrievalMethodConfig: FC<Props> = ({
return (
<div className='space-y-2'>
{supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
<RadioCard
icon={<Semantic className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.vector} alt='' />}
title={t('dataset.retrieval.semantic_search.title')}
description={t('dataset.retrieval.semantic_search.description')}
isChosen={value.search_method === RETRIEVE_METHOD.semantic}
onChosen={() => onChange({
isActive={
value.search_method === RETRIEVE_METHOD.semantic
}
onClick={() => onChange({
...value,
search_method: RETRIEVE_METHOD.semantic,
})}
chosenConfig={
<RetrievalParamConfig
type={RETRIEVE_METHOD.semantic}
value={value}
onChange={onChange}
/>
}
/>
effectImg={Effect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.semantic}
value={value}
onChange={onChange}
/>
</OptionCard>
)}
{supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
<RadioCard
icon={<FileSearch02 className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.fullText} alt='' />}
title={t('dataset.retrieval.full_text_search.title')}
description={t('dataset.retrieval.full_text_search.description')}
isChosen={value.search_method === RETRIEVE_METHOD.fullText}
onChosen={() => onChange({
isActive={
value.search_method === RETRIEVE_METHOD.fullText
}
onClick={() => onChange({
...value,
search_method: RETRIEVE_METHOD.fullText,
})}
chosenConfig={
<RetrievalParamConfig
type={RETRIEVE_METHOD.fullText}
value={value}
onChange={onChange}
/>
}
/>
effectImg={Effect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.fullText}
value={value}
onChange={onChange}
/>
</OptionCard>
)}
{supportRetrievalMethods.includes(RETRIEVE_METHOD.semantic) && (
<RadioCard
icon={<PatternRecognition className='w-4 h-4 text-[#7839EE]' />}
<OptionCard icon={<Image className='w-4 h-4' src={retrievalIcon.hybrid} alt='' />}
title={
<div className='flex items-center space-x-1'>
<div>{t('dataset.retrieval.hybrid_search.title')}</div>
<div className='flex h-full items-center px-1.5 rounded-md border border-[#E0EAFF] text-xs font-medium text-[#444CE7]'>{t('dataset.retrieval.hybrid_search.recommend')}</div>
<Badge text={t('dataset.retrieval.hybrid_search.recommend')} className='border-text-accent-secondary text-text-accent-secondary ml-2' uppercase />
</div>
}
description={t('dataset.retrieval.hybrid_search.description')}
isChosen={value.search_method === RETRIEVE_METHOD.hybrid}
onChosen={() => onChange({
description={t('dataset.retrieval.hybrid_search.description')} isActive={
value.search_method === RETRIEVE_METHOD.hybrid
}
onClick={() => onChange({
...value,
search_method: RETRIEVE_METHOD.hybrid,
reranking_enable: true,
})}
chosenConfig={
<RetrievalParamConfig
type={RETRIEVE_METHOD.hybrid}
value={value}
onChange={onChange}
/>
}
/>
effectImg={Effect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F0EEFA] to-[#F9FAFB]'
>
<RetrievalParamConfig
type={RETRIEVE_METHOD.hybrid}
value={value}
onChange={onChange}
/>
</OptionCard>
)}
</div>
)

View File

@@ -32,7 +32,7 @@ const EconomicalRetrievalMethodConfig: FC<Props> = ({
return (
<div className='space-y-2'>
<RadioCard
icon={<Icon className='w-4 h-4 text-[#7839EE]' />}
icon={<Icon className='w-4 h-4 text-util-colors-purple-purple-600' />}
title={t(`dataset.retrieval.${type}.title`)}
description={t(`dataset.retrieval.${type}.description`)}
noRadio

View File

@@ -3,6 +3,9 @@ import type { FC } from 'react'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Image from 'next/image'
import ProgressIndicator from '../../create/assets/progress-indicator.svg'
import Reranking from '../../create/assets/rerank.svg'
import cn from '@/utils/classnames'
import TopKItem from '@/app/components/base/param-item/top-k-item'
import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
@@ -20,6 +23,7 @@ import {
} from '@/models/datasets'
import WeightedScore from '@/app/components/app/configuration/dataset-config/params-config/weighted-score'
import Toast from '@/app/components/base/toast'
import RadioCard from '@/app/components/base/radio-card'
type Props = {
type: RETRIEVE_METHOD
@@ -201,24 +205,22 @@ const RetrievalParamConfig: FC<Props> = ({
{
isHybridSearch && (
<>
<div className='flex items-center justify-between'>
<div className='flex gap-2 mb-4'>
{
rerankingModeOptions.map(option => (
<div
<RadioCard
key={option.value}
className={cn(
'flex items-center justify-center mb-4 w-[calc((100%-8px)/2)] h-8 rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg cursor-pointer system-sm-medium text-text-secondary',
value.reranking_mode === RerankingModeEnum.WeightedScore && option.value === RerankingModeEnum.WeightedScore && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
value.reranking_mode !== RerankingModeEnum.WeightedScore && option.value !== RerankingModeEnum.WeightedScore && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
)}
onClick={() => handleChangeRerankMode(option.value)}
>
<div className='truncate'>{option.label}</div>
<Tooltip
popupContent={<div className='w-[200px]'>{option.tips}</div>}
triggerClassName='ml-0.5 w-3.5 h-3.5'
/>
</div>
isChosen={value.reranking_mode === option.value}
onChosen={() => handleChangeRerankMode(option.value)}
icon={<Image src={
option.value === RerankingModeEnum.WeightedScore
? ProgressIndicator
: Reranking
} alt=''/>}
title={option.label}
description={option.tips}
className='flex-1'
/>
))
}
</div>

View File

@@ -0,0 +1,6 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.18055 6.45828C7.52291 6.45828 8.61111 5.37008 8.61111 4.02772C8.61111 2.68536 7.52291 1.59717 6.18055 1.59717C4.8382 1.59717 3.75 2.68536 3.75 4.02772C3.75 5.37008 4.8382 6.45828 6.18055 6.45828Z" fill="#EF6820"/>
<path d="M13.8192 6.45828C15.1616 6.45828 16.2498 5.37008 16.2498 4.02772C16.2498 2.68536 15.1616 1.59717 13.8192 1.59717C12.4769 1.59717 11.3887 2.68536 11.3887 4.02772C11.3887 5.37008 12.4769 6.45828 13.8192 6.45828Z" fill="#EF6820"/>
<path d="M13.8193 7.84719C13.0627 7.84805 12.3185 8.03933 11.6552 8.40341C10.992 8.7675 10.4311 9.29267 10.0241 9.93053C10.5745 9.93695 11.1 10.1609 11.4858 10.5535C11.8716 10.9461 12.0864 11.4755 12.0831 12.0259C12.0799 12.5763 11.859 13.1031 11.4687 13.4911C11.0783 13.8792 10.5503 14.097 9.99984 14.097C9.44942 14.097 8.92135 13.8792 8.53101 13.4911C8.14066 13.1031 7.91976 12.5763 7.91655 12.0259C7.91334 11.4755 8.12808 10.9461 8.51387 10.5535C8.89966 10.1609 9.42515 9.93695 9.97554 9.93053C9.45127 9.10686 8.67371 8.47572 7.75983 8.13205C6.84596 7.78839 5.84519 7.75078 4.9081 8.0249C3.97101 8.29902 3.14828 8.87003 2.56368 9.65203C1.97908 10.434 1.66424 11.3847 1.66652 12.3611V16.875C1.66652 17.0591 1.73968 17.2358 1.86991 17.366C2.00015 17.4962 2.17678 17.5694 2.36096 17.5694H7.22207V15.8333L4.72207 13.9583C4.64911 13.9036 4.58765 13.835 4.54118 13.7566C4.49472 13.6781 4.46417 13.5912 4.45127 13.501C4.42522 13.3186 4.47267 13.1334 4.58318 12.9861C4.69369 12.8387 4.8582 12.7413 5.04053 12.7153C5.22285 12.6892 5.40806 12.7367 5.5554 12.8472L8.14776 14.7916H11.8519L14.4443 12.8472C14.5916 12.7367 14.7768 12.6892 14.9592 12.7153C15.1415 12.7413 15.306 12.8387 15.4165 12.9861C15.527 13.1334 15.5745 13.3186 15.5484 13.501C15.5224 13.6833 15.425 13.8478 15.2776 13.9583L12.7776 15.8333V17.5694H17.6387C17.8229 17.5694 17.9995 17.4962 18.1298 17.366C18.26 17.2358 18.3332 17.0591 18.3332 16.875V12.3611C18.3317 11.1644 17.8557 10.0171 17.0095 9.17091C16.1633 8.32471 15.016 7.84867 13.8193 7.84719Z" fill="#EF6820"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="file-list-3-fill">
<path id="Vector" d="M15.8332 18.3333H4.1665C2.7858 18.3333 1.6665 17.2141 1.6665 15.8333V2.50001C1.6665 2.03977 2.0396 1.66667 2.49984 1.66667H14.1665C14.6268 1.66667 14.9998 2.03977 14.9998 2.50001V12.5H18.3332V15.8333C18.3332 17.2141 17.2139 18.3333 15.8332 18.3333ZM14.9998 14.1667V15.8333C14.9998 16.2936 15.3729 16.6667 15.8332 16.6667C16.2934 16.6667 16.6665 16.2936 16.6665 15.8333V14.1667H14.9998ZM4.99984 5.83334V7.50001H11.6665V5.83334H4.99984ZM4.99984 9.16667V10.8333H11.6665V9.16667H4.99984ZM4.99984 12.5V14.1667H9.1665V12.5H4.99984Z" fill="#1570EF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 699 B

View File

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.99984 1.66663C8.35166 1.66663 6.7405 2.15537 5.37009 3.07105C3.99968 3.98672 2.93157 5.28821 2.30084 6.81093C1.67011 8.33365 1.50509 10.0092 1.82663 11.6257C2.14817 13.2422 2.94185 14.7271 4.10728 15.8925C5.27272 17.058 6.75758 17.8516 8.37409 18.1732C9.9906 18.4947 11.6662 18.3297 13.1889 17.699C14.7116 17.0682 16.0131 16.0001 16.9288 14.6297C17.8444 13.2593 18.3332 11.6481 18.3332 9.99996C18.3332 7.78982 17.4552 5.67021 15.8924 4.1074C14.3296 2.5446 12.21 1.66663 9.99984 1.66663ZM12.295 5.65899L13.1116 4.53538C13.1653 4.46155 13.2329 4.39901 13.3107 4.35133C13.3885 4.30365 13.4749 4.27175 13.565 4.25747C13.6551 4.24319 13.7472 4.24679 13.8359 4.26809C13.9246 4.28938 14.0083 4.32793 14.0821 4.38156C14.156 4.43518 14.2185 4.50282 14.2662 4.58061C14.3139 4.6584 14.3458 4.74482 14.36 4.83494C14.3743 4.92506 14.3707 5.01711 14.3494 5.10583C14.3281 5.19456 14.2896 5.27822 14.236 5.35204L13.4193 6.47565C13.311 6.62474 13.1479 6.72471 12.9659 6.75356C12.7839 6.7824 12.5979 6.73777 12.4488 6.62947C12.2997 6.52118 12.1997 6.35809 12.1709 6.17609C12.142 5.99408 12.1867 5.80808 12.295 5.65899ZM5.9179 4.3819C5.99174 4.32795 6.07551 4.28911 6.1644 4.26761C6.25329 4.24612 6.34556 4.2424 6.43589 4.25666C6.52623 4.27092 6.61286 4.30288 6.69081 4.35071C6.76875 4.39854 6.83649 4.4613 6.89012 4.53538L7.70817 5.65899C7.81647 5.80854 7.86092 5.99499 7.83175 6.17731C7.80258 6.35964 7.70217 6.52291 7.55262 6.63121C7.40307 6.73951 7.21662 6.78396 7.03429 6.75478C6.85196 6.72561 6.68869 6.62521 6.5804 6.47565L5.76373 5.35204C5.71013 5.27823 5.6716 5.19457 5.65034 5.10586C5.62908 5.01715 5.62551 4.92512 5.63983 4.83503C5.65414 4.74494 5.68607 4.65855 5.73378 4.5808C5.78149 4.50306 5.84406 4.43547 5.9179 4.3819ZM5.59151 12.1597L4.27206 12.5888C4.18433 12.6215 4.0909 12.6361 3.99739 12.6317C3.90388 12.6273 3.81222 12.6041 3.72791 12.5634C3.64361 12.5227 3.56841 12.4654 3.50682 12.3949C3.44524 12.3244 3.39854 12.2421 3.36954 12.1531C3.34055 12.0641 3.32984 11.9702 3.33808 11.8769C3.34631 11.7837 3.37332 11.693 3.41747 11.6105C3.46162 11.528 3.522 11.4552 3.59499 11.3966C3.66798 11.3379 3.75207 11.2947 3.8422 11.2694L5.16165 10.8402C5.24947 10.8072 5.34308 10.7924 5.43681 10.7965C5.53054 10.8007 5.62245 10.8238 5.707 10.8645C5.79154 10.9052 5.86697 10.9626 5.92872 11.0332C5.99047 11.1039 6.03727 11.1863 6.06629 11.2755C6.09531 11.3647 6.10595 11.4589 6.09757 11.5524C6.08919 11.6458 6.06195 11.7366 6.01752 11.8192C5.97308 11.9018 5.91236 11.9746 5.83902 12.0331C5.76568 12.0916 5.68194 12.1347 5.59151 12.1597ZM10.6943 16.25C10.6943 16.4341 10.6211 16.6108 10.4909 16.741C10.3607 16.8712 10.184 16.9444 9.99984 16.9444C9.81566 16.9444 9.63903 16.8712 9.50879 16.741C9.37856 16.6108 9.3054 16.4341 9.3054 16.25V14.8611C9.3054 14.6769 9.37856 14.5003 9.50879 14.37C9.63903 14.2398 9.81566 14.1666 9.99984 14.1666C10.184 14.1666 10.3607 14.2398 10.4909 14.37C10.6211 14.5003 10.6943 14.6769 10.6943 14.8611V16.25ZM9.99984 12.2222L7.38595 13.5972L7.88526 10.6868L5.77067 8.62565L8.6929 8.20135L9.99984 5.55551L11.3068 8.20135L14.229 8.62565L12.1144 10.6868L12.6137 13.5972L9.99984 12.2222ZM15.729 12.5902L14.4096 12.1611C14.3191 12.1361 14.2347 12.093 14.1614 12.0345C14.088 11.976 14.0273 11.9032 13.9829 11.8206C13.9384 11.738 13.9112 11.6472 13.9028 11.5537C13.8944 11.4603 13.9051 11.3661 13.9341 11.2769C13.9631 11.1877 14.0099 11.1053 14.0717 11.0346C14.1334 10.964 14.2088 10.9066 14.2934 10.8659C14.3779 10.8252 14.4698 10.8021 14.5636 10.7979C14.6573 10.7938 14.7509 10.8086 14.8387 10.8416L16.1582 11.2708C16.2483 11.2961 16.3324 11.3393 16.4054 11.398C16.4784 11.4566 16.5388 11.5293 16.5829 11.6119C16.6271 11.6944 16.6541 11.7851 16.6623 11.8783C16.6705 11.9716 16.6598 12.0655 16.6308 12.1545C16.6018 12.2435 16.5551 12.3258 16.4936 12.3963C16.432 12.4668 16.3568 12.5241 16.2725 12.5648C16.1882 12.6055 16.0965 12.6287 16.003 12.6331C15.9095 12.6375 15.8167 12.6229 15.729 12.5902Z" fill="#EF6820"/>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="note-mod">
<path id="Vector" d="M17.6387 3.05555H2.36095C1.97762 3.05555 1.6665 3.36666 1.6665 3.74999V16.25C1.6665 16.6333 1.97762 16.9444 2.36095 16.9444H17.6387C18.0221 16.9444 18.3332 16.6333 18.3332 16.25V3.74999C18.3332 3.36666 18.0221 3.05555 17.6387 3.05555ZM9.30539 14.1667H5.13873C4.75539 14.1667 4.44428 13.8555 4.44428 13.4722C4.44428 13.0889 4.75539 12.7778 5.13873 12.7778H9.30539C9.68873 12.7778 9.99984 13.0889 9.99984 13.4722C9.99984 13.8555 9.68873 14.1667 9.30539 14.1667ZM14.8609 10.6944H5.13873C4.75539 10.6944 4.44428 10.3833 4.44428 9.99999C4.44428 9.61666 4.75539 9.30555 5.13873 9.30555H14.8609C15.2443 9.30555 15.5554 9.61666 15.5554 9.99999C15.5554 10.3833 15.2443 10.6944 14.8609 10.6944ZM14.8609 7.22221H5.13873C4.75539 7.22221 4.44428 6.9111 4.44428 6.52777C4.44428 6.14443 4.75539 5.83332 5.13873 5.83332H14.8609C15.2443 5.83332 15.5554 6.14443 15.5554 6.52777C15.5554 6.9111 15.2443 7.22221 14.8609 7.22221Z" fill="#1570EF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,12 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.8" filter="url(#filter0_f_1328_28605)">
<circle cx="32" cy="32" r="28" fill="#444CE7"/>
</g>
<defs>
<filter id="filter0_f_1328_28605" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_1328_28605"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@@ -0,0 +1,12 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.8" filter="url(#filter0_f_481_16338)">
<circle cx="32" cy="32" r="28" fill="#EF6820"/>
</g>
<defs>
<filter id="filter0_f_481_16338" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_481_16338"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 610 B

View File

@@ -0,0 +1,12 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.8" filter="url(#filter0_f_481_16453)">
<circle cx="32" cy="32" r="28" fill="#6938EF"/>
</g>
<defs>
<filter id="filter0_f_481_16453" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_481_16453"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 610 B

View File

@@ -0,0 +1,12 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.93923 18.3333C3.48973 18.3333 3.05032 18.2 2.67657 17.9503C2.30282 17.7006 2.01152 17.3456 1.83951 16.9303C1.66749 16.515 1.62248 16.0581 1.71017 15.6172C1.79787 15.1763 2.01432 14.7714 2.33217 14.4535C2.65002 14.1357 3.05498 13.9192 3.49584 13.8315C3.93671 13.7438 4.39368 13.7889 4.80897 13.9609C5.22425 14.1329 5.5792 14.4242 5.82894 14.7979C6.07867 15.1717 6.21196 15.6111 6.21196 16.0606C6.21196 16.6634 5.97251 17.2414 5.54629 17.6677C5.12007 18.0939 4.542 18.3333 3.93923 18.3333Z" fill="#6938EF"/>
<path d="M9.99978 7.72726C9.55028 7.72726 9.11087 7.86056 8.73712 8.11029C8.36337 8.36002 8.07207 8.71497 7.90005 9.13026C7.72804 9.54554 7.68303 10.0025 7.77072 10.4434C7.85842 10.8842 8.07487 11.2892 8.39272 11.6071C8.71056 11.9249 9.11553 12.1414 9.55639 12.229C9.99726 12.3167 10.4542 12.2717 10.8695 12.0997C11.2848 11.9277 11.6398 11.6364 11.8895 11.2627C12.1392 10.8889 12.2725 10.4495 12.2725 9.99999C12.2725 9.39723 12.0331 8.81915 11.6068 8.39293C11.1806 7.96671 10.6025 7.72726 9.99978 7.72726Z" fill="#6938EF"/>
<path d="M3.93923 1.66666C3.48973 1.66666 3.05032 1.79995 2.67657 2.04968C2.30282 2.29941 2.01152 2.65436 1.83951 3.06965C1.66749 3.48494 1.62248 3.9419 1.71017 4.38277C1.79787 4.82364 2.01432 5.2286 2.33217 5.54644C2.65002 5.86429 3.05498 6.08075 3.49585 6.16844C3.93671 6.25613 4.39368 6.21113 4.80897 6.03911C5.22425 5.86709 5.57921 5.57579 5.82894 5.20204C6.07867 4.8283 6.21196 4.38889 6.21196 3.93938C6.21196 3.33662 5.97251 2.75854 5.54629 2.33232C5.12007 1.9061 4.542 1.66666 3.93923 1.66666Z" fill="#6938EF"/>
<path d="M16.0603 1.66666C15.6108 1.66666 15.1714 1.79995 14.7977 2.04968C14.4239 2.29941 14.1326 2.65436 13.9606 3.06965C13.7886 3.48494 13.7436 3.9419 13.8313 4.38277C13.919 4.82364 14.1354 5.2286 14.4533 5.54644C14.7711 5.86429 15.1761 6.08075 15.6169 6.16844C16.0578 6.25613 16.5148 6.21113 16.9301 6.03911C17.3453 5.86709 17.7003 5.57579 17.95 5.20204C18.1998 4.8283 18.3331 4.38889 18.3331 3.93938C18.3331 3.33662 18.0936 2.75854 17.6674 2.33232C17.2412 1.9061 16.6631 1.66666 16.0603 1.66666Z" fill="#6938EF"/>
<path d="M16.0603 13.7879C15.6108 13.7879 15.1714 13.9212 14.7977 14.1709C14.4239 14.4206 14.1326 14.7756 13.9606 15.1909C13.7886 15.6062 13.7436 16.0631 13.8313 16.504C13.919 16.9449 14.1354 17.3498 14.4533 17.6677C14.7711 17.9855 15.1761 18.202 15.6169 18.2897C16.0578 18.3774 16.5148 18.3323 16.9301 18.1603C17.3453 17.9883 17.7003 17.697 17.95 17.3233C18.1998 16.9495 18.3331 16.5101 18.3331 16.0606C18.3331 15.4578 18.0936 14.8798 17.6674 14.4535C17.2412 14.0273 16.6631 13.7879 16.0603 13.7879Z" fill="#6938EF"/>
<path d="M6.21196 7.72726H1.6665V12.2727H6.21196V7.72726Z" fill="#6938EF"/>
<path d="M18.3331 7.72726H13.7876V12.2727H18.3331V7.72726Z" fill="#6938EF"/>
<path d="M12.2725 1.66666H7.72705V6.21211H12.2725V1.66666Z" fill="#6938EF"/>
<path d="M12.2725 13.7879H7.72705V18.3333H12.2725V13.7879Z" fill="#6938EF"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,7 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.91672 15.2028V17.9805H6.52783V15.2028H7.91672Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1667 15.2028V17.9805H12.7778V15.2028H14.1667Z" fill="#444CE7"/>
<path d="M14.1666 2.0083C14.1666 3.54243 12.923 4.78608 11.3889 4.78608C9.85476 4.78608 8.61108 3.54243 8.61108 2.0083L14.1666 2.0083Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.8864 5.23918C6.9718 4.92907 8.12598 5.30791 8.81883 6.17498H13.1251C16.0015 6.17498 18.3334 8.50683 18.3334 11.3833C18.3334 14.2598 16.0015 16.5916 13.1251 16.5916H7.39252C6.14908 16.5916 4.97062 16.0363 4.1791 15.0773L3.32342 14.0407L1.66675 13.3448V9.93061L3.65692 9.40957L4.44453 8.40703V5.65114L5.8864 5.23918ZM8.61119 8.25831H14.1667V9.64721H8.61119V8.25831ZM6.52786 9.99443C6.52786 10.5697 6.06149 11.0361 5.48619 11.0361C4.91089 11.0361 4.44453 10.5697 4.44453 9.99443C4.44453 9.41915 4.91089 8.95276 5.48619 8.95276C6.06149 8.95276 6.52786 9.41915 6.52786 9.99443Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,8 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="progress-indicator">
<g id="Vector">
<path d="M18.4029 10.7639H1.59738C1.17572 10.7639 0.833496 11.1061 0.833496 11.5278V16.1111C0.833496 16.5328 1.17572 16.875 1.59738 16.875H18.4029C18.8246 16.875 19.1668 16.5328 19.1668 16.1111V11.5278C19.1668 11.1061 18.8246 10.7639 18.4029 10.7639ZM17.6391 15.3472H10.0002V12.2917H17.6391V15.3472Z" fill="#1570EF"/>
<path d="M9.716 7.58153C9.78933 7.66174 9.89169 7.70833 10.0002 7.70833C10.1086 7.70833 10.211 7.6625 10.2843 7.58153L13.7218 3.76208C13.8227 3.64979 13.8479 3.48937 13.7868 3.35111C13.7249 3.21361 13.5881 3.125 13.4377 3.125H6.56266C6.41218 3.125 6.27544 3.21361 6.21356 3.35111C6.15245 3.48937 6.17766 3.64979 6.2785 3.76208L9.716 7.58153Z" fill="#1570EF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 835 B

View File

@@ -0,0 +1,13 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="rerank">
<g id="Vector">
<path d="M18.3333 4.58329C18.3333 5.73389 17.4005 6.66663 16.2499 6.66663C15.0993 6.66663 14.1666 5.73389 14.1666 4.58329C14.1666 3.4327 15.0993 2.49996 16.2499 2.49996C17.4005 2.49996 18.3333 3.4327 18.3333 4.58329Z" fill="#0E9384"/>
<path d="M13.3333 15.4166C13.3333 16.5672 12.4005 17.5 11.2499 17.5C10.0993 17.5 9.16658 16.5672 9.16658 15.4166C9.16658 14.266 10.0993 13.3333 11.2499 13.3333C12.4005 13.3333 13.3333 14.266 13.3333 15.4166Z" fill="#0E9384"/>
<path d="M12.0833 4.58329C12.0833 5.27365 11.5236 5.83329 10.8333 5.83329C10.1429 5.83329 9.58325 5.27365 9.58325 4.58329C9.58325 3.89294 10.1429 3.33329 10.8333 3.33329C11.5236 3.33329 12.0833 3.89294 12.0833 4.58329Z" fill="#0E9384"/>
<path d="M17.4999 15.4166C17.4999 16.107 16.9403 16.6666 16.2499 16.6666C15.5596 16.6666 14.9999 16.107 14.9999 15.4166C14.9999 14.7263 15.5596 14.1666 16.2499 14.1666C16.9403 14.1666 17.4999 14.7263 17.4999 15.4166Z" fill="#0E9384"/>
<path d="M7.49992 15.4166C7.49992 17.0275 6.19408 18.3333 4.58325 18.3333C2.97242 18.3333 1.66659 17.0275 1.66659 15.4166C1.66659 13.8058 2.97242 12.5 4.58325 12.5C6.19408 12.5 7.49992 13.8058 7.49992 15.4166Z" fill="#0E9384"/>
<path d="M7.49992 4.58329C7.49992 6.19412 6.19408 7.49996 4.58325 7.49996C2.97242 7.49996 1.66659 6.19412 1.66659 4.58329C1.66659 2.97246 2.97242 1.66663 4.58325 1.66663C6.19408 1.66663 7.49992 2.97246 7.49992 4.58329Z" fill="#0E9384"/>
<path d="M0.833252 9.99996C0.833252 9.53972 1.20635 9.16663 1.66659 9.16663H18.3333C18.7935 9.16663 19.1666 9.53972 19.1666 9.99996C19.1666 10.4602 18.7935 10.8333 18.3333 10.8333H1.66659C1.20635 10.8333 0.833252 10.4602 0.833252 9.99996Z" fill="#0E9384"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,6 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.6752 4.83333H9.91553V1.07361L13.6752 4.83333Z" fill="#6938EF"/>
<path d="M7.2003 13.8611H2.62391C2.53183 13.8611 2.44351 13.8245 2.37839 13.7594C2.31327 13.6943 2.27669 13.606 2.27669 13.5139V12.8195C2.27669 12.7274 2.31327 12.6391 2.37839 12.5739C2.44351 12.5088 2.53183 12.4722 2.62391 12.4722H7.2003C7.33176 11.6964 7.68097 10.9739 8.20725 10.3889H2.62391C2.53183 10.3889 2.44351 10.3523 2.37839 10.2872C2.31327 10.2221 2.27669 10.1338 2.27669 10.0417V9.34724C2.27669 9.25515 2.31327 9.16684 2.37839 9.10172C2.44351 9.0366 2.53183 9.00002 2.62391 9.00002H11.3045C12.3309 9.0003 13.3207 9.38137 14.0822 10.0695V6.22224H9.22114C9.03696 6.22224 8.86032 6.14908 8.73009 6.01884C8.59986 5.88861 8.52669 5.71198 8.52669 5.5278V0.666687H0.887804C0.703626 0.666687 0.526991 0.739851 0.396757 0.870085C0.266524 1.00032 0.193359 1.17695 0.193359 1.36113V16.6389C0.193359 16.8231 0.266524 16.9997 0.396757 17.13C0.526991 17.2602 0.703626 17.3334 0.887804 17.3334H10.61C9.73337 17.224 8.91945 16.8214 8.30046 16.1911C7.68146 15.5607 7.29375 14.7396 7.2003 13.8611ZM2.62391 5.5278H6.09614C6.18823 5.5278 6.27654 5.56438 6.34166 5.6295C6.40678 5.69461 6.44336 5.78293 6.44336 5.87502V6.56947C6.44336 6.66155 6.40678 6.74987 6.34166 6.81499C6.27654 6.88011 6.18823 6.91669 6.09614 6.91669H2.62391C2.53183 6.91669 2.44351 6.88011 2.37839 6.81499C2.31327 6.74987 2.27669 6.66155 2.27669 6.56947V5.87502C2.27669 5.78293 2.31327 5.69461 2.37839 5.6295C2.44351 5.56438 2.53183 5.5278 2.62391 5.5278Z" fill="#6938EF"/>
<path d="M15.2678 16.1479L13.6887 14.5688C13.9439 14.1455 14.08 13.661 14.0824 13.1667C14.0824 12.6173 13.9195 12.0802 13.6143 11.6234C13.309 11.1666 12.8752 10.8106 12.3676 10.6004C11.8601 10.3901 11.3016 10.3351 10.7627 10.4423C10.2239 10.5495 9.72893 10.814 9.34045 11.2025C8.95197 11.591 8.68741 12.0859 8.58023 12.6248C8.47305 13.1636 8.52806 13.7221 8.7383 14.2297C8.94855 14.7373 9.30458 15.1711 9.76138 15.4763C10.2182 15.7816 10.7552 15.9445 11.3046 15.9445C11.799 15.9421 12.2834 15.806 12.7067 15.5507L14.2859 17.1299C14.4169 17.2564 14.5923 17.3264 14.7744 17.3248C14.9564 17.3232 15.1306 17.2502 15.2594 17.1214C15.3881 16.9927 15.4612 16.8185 15.4627 16.6364C15.4643 16.4543 15.3943 16.2789 15.2678 16.1479ZM9.91575 13.1667C9.91575 12.892 9.9972 12.6235 10.1498 12.3951C10.3024 12.1667 10.5193 11.9887 10.7731 11.8835C11.0269 11.7784 11.3062 11.7509 11.5756 11.8045C11.845 11.8581 12.0925 11.9904 12.2867 12.1846C12.481 12.3788 12.6132 12.6263 12.6668 12.8957C12.7204 13.1652 12.6929 13.4444 12.5878 13.6982C12.4827 13.952 12.3047 14.1689 12.0763 14.3215C11.8479 14.4741 11.5793 14.5556 11.3046 14.5556C10.9363 14.5556 10.583 14.4093 10.3225 14.1488C10.0621 13.8883 9.91575 13.5351 9.91575 13.1667Z" fill="#6938EF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,12 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83317 18.3334H1.6665V14.1667H5.83317V18.3334Z" fill="#6938EF"/>
<path d="M12.0832 12.0834H7.9165V7.91669H12.0832V12.0834Z" fill="#6938EF"/>
<path d="M5.83317 12.0834H1.6665V7.91669H5.83317V12.0834Z" fill="#6938EF"/>
<path d="M12.0832 5.83335H7.9165V1.66669H12.0832V5.83335Z" fill="#6938EF"/>
<path d="M5.83317 5.83335H1.6665V1.66669H5.83317V5.83335Z" fill="#6938EF"/>
<path d="M18.3332 5.83335H14.1665V1.66669H18.3332V5.83335Z" fill="#6938EF"/>
<path d="M17.6386 14.8611H14.8608V17.6389H17.6386V14.8611Z" fill="#6938EF"/>
<path d="M17.6386 8.61115H14.8608V11.3889H17.6386V8.61115Z" fill="#6938EF"/>
<path d="M11.3886 14.8611H8.61084V17.6389H11.3886V14.8611Z" fill="#6938EF"/>
</svg>

After

Width:  |  Height:  |  Size: 835 B

View File

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0002 0.833374C10.4604 0.833374 10.8335 1.20647 10.8335 1.66671V2.54597C11.5977 2.63056 12.328 2.8301 13.0061 3.12703L13.4452 2.36666C13.6752 1.96808 14.1849 1.83152 14.5835 2.06164C14.9821 2.29176 15.1186 2.80142 14.8885 3.2L14.4488 3.96146C15.0552 4.40877 15.5915 4.94516 16.0388 5.55143L16.8003 5.11177C17.1989 4.88165 17.7086 5.01821 17.9387 5.41679C18.1688 5.81537 18.0322 6.32502 17.6337 6.55514L16.8732 6.99418C17.1702 7.67226 17.3697 8.40254 17.4543 9.16679H18.3335C18.7937 9.16679 19.1668 9.53987 19.1668 10.0001C19.1668 10.4604 18.7937 10.8335 18.3335 10.8335H17.4543C17.3697 11.5977 17.1702 12.328 16.8732 13.0061L17.6337 13.4452C18.0322 13.6753 18.1688 14.185 17.9387 14.5835C17.7086 14.9821 17.1989 15.1187 16.8003 14.8885L16.0388 14.4489C15.5915 15.0551 15.0551 15.5915 14.4488 16.0388L14.8885 16.8004C15.1186 17.1989 14.9821 17.7085 14.5835 17.9387C14.1849 18.1688 13.6752 18.0322 13.4452 17.6337L13.0061 16.8732C12.328 17.1701 11.5977 17.3697 10.8335 17.4543V18.3334C10.8335 18.7936 10.4604 19.1667 10.0002 19.1667C9.53991 19.1667 9.16683 18.7936 9.16683 18.3334V17.4543C8.40258 17.3697 7.6723 17.1701 6.99424 16.8732L6.55516 17.6337C6.32505 18.0323 5.81539 18.1689 5.41681 17.9388C5.01824 17.7086 4.88167 17.199 5.11179 16.8005L5.55149 16.0388C4.94519 15.5915 4.40878 15.0551 3.96145 14.4488L3.19993 14.8885C2.80135 15.1186 2.2917 14.982 2.06158 14.5835C1.83145 14.1849 1.96802 13.6752 2.3666 13.4451L3.12704 13.006C2.83011 12.328 2.63056 11.5977 2.54598 10.8335L1.66679 10.8334C1.20655 10.8334 0.833474 10.4602 0.833496 10C0.833521 9.53979 1.20663 9.16671 1.66687 9.16671L2.54599 9.16679C2.63058 8.40254 2.8301 7.67229 3.12701 6.99424L2.3666 6.55523C1.96802 6.32512 1.83145 5.81546 2.06157 5.41687C2.29169 5.0183 2.80135 4.88173 3.19992 5.11185L3.96142 5.55148C4.40874 4.94518 4.94515 4.40877 5.55145 3.96144L5.11179 3.19991C4.88167 2.80133 5.01823 2.29167 5.41681 2.06156C5.81539 1.83144 6.32505 1.968 6.55516 2.36657L6.9942 3.12702C7.67228 2.83009 8.40258 2.63055 9.16683 2.54597V1.66671C9.16683 1.20647 9.53991 0.833374 10.0002 0.833374ZM6.39156 5.41655C5.81089 5.87442 5.31917 6.44029 4.94695 7.08361C4.45095 7.94087 4.16681 8.93604 4.16681 10.0001C4.16681 11.0642 4.45096 12.0594 4.94698 12.9167C5.3192 13.56 5.81091 14.1259 6.39159 14.5837L8.1 11.6246C7.72651 11.1881 7.50015 10.6208 7.50015 10.0001C7.50015 9.37946 7.72651 8.81212 8.09999 8.37562L6.39156 5.41655ZM9.54316 7.54194L7.83418 4.5819C8.50325 4.31416 9.23383 4.16679 10.0002 4.16679C11.0642 4.16679 12.0594 4.45095 12.9167 4.94697C13.8022 5.45932 14.541 6.19807 15.0533 7.08357C15.4173 7.71277 15.6673 8.41629 15.7745 9.16679H12.3579C12.0147 8.19579 11.0887 7.50012 10.0002 7.50012C9.84433 7.50012 9.69149 7.51446 9.54316 7.54194ZM12.3579 10.8335C12.0147 11.8045 11.0887 12.5001 10.0002 12.5001C9.84433 12.5001 9.69149 12.4858 9.54316 12.4583L7.8342 15.4184C8.50325 15.6861 9.23383 15.8335 10.0002 15.8335C11.0642 15.8335 12.0594 15.5493 12.9167 15.0533C13.8022 14.541 14.5409 13.8022 15.0532 12.9167C15.4173 12.2875 15.6673 11.584 15.7745 10.8335H12.3579Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -7,7 +7,11 @@ import { omit } from 'lodash-es'
import { ArrowRightIcon } from '@heroicons/react/24/solid'
import {
RiErrorWarningFill,
RiLoader2Fill,
RiTerminalBoxLine,
} from '@remixicon/react'
import Image from 'next/image'
import { indexMethodIcon, retrievalIcon } from '../icons'
import s from './index.module.css'
import cn from '@/utils/classnames'
import { FieldInfo } from '@/app/components/datasets/documents/detail/metadata'
@@ -23,15 +27,21 @@ import UpgradeBtn from '@/app/components/billing/upgrade-btn'
import { useProviderContext } from '@/context/provider-context'
import Tooltip from '@/app/components/base/tooltip'
import { sleep } from '@/utils'
import { RETRIEVE_METHOD } from '@/types/app'
type Props = {
datasetId: string
batchId: string
documents?: FullDocumentDetail[]
indexingType?: string
retrievalMethod?: string
}
const RuleDetail: FC<{ sourceData?: ProcessRuleResponse }> = ({ sourceData }) => {
const RuleDetail: FC<{
sourceData?: ProcessRuleResponse
indexingType?: string
retrievalMethod?: string
}> = ({ sourceData, indexingType, retrievalMethod }) => {
const { t } = useTranslation()
const segmentationRuleMap = {
@@ -81,10 +91,40 @@ const RuleDetail: FC<{ sourceData?: ProcessRuleResponse }> = ({ sourceData }) =>
displayedValue={String(getValue(field))}
/>
})}
<FieldInfo
label={t('datasetCreation.stepTwo.indexMode')}
displayedValue={t(`datasetCreation.stepTwo.${indexingType}`) as string}
valueIcon={
<Image
src={
indexingType === 'economy'
? indexMethodIcon.economical
: indexMethodIcon.high_quality
}
alt=''
/>
}
/>
<FieldInfo
label={t('datasetSettings.form.retrievalSetting.title')}
displayedValue={t(`datasetSettings.form.retrievalSetting.${retrievalMethod}`) as string}
valueIcon={
<Image
src={
retrievalMethod === RETRIEVE_METHOD.fullText
? retrievalIcon.fullText
: RETRIEVE_METHOD.semantic
? retrievalIcon.vector
: retrievalIcon.hybrid
}
alt=''
/>
}
/>
</div>
}
const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], indexingType }) => {
const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], indexingType, retrievalMethod }) => {
const { t } = useTranslation()
const { enableBilling, plan } = useProviderContext()
@@ -146,6 +186,9 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
const navToDocumentList = () => {
router.push(`/datasets/${datasetId}/documents`)
}
const navToApiDocs = () => {
router.push('/datasets?category=api')
}
const isEmbedding = useMemo(() => {
return indexingStatusBatchDetail.some(indexingStatusDetail => ['indexing', 'splitting', 'parsing', 'cleaning'].includes(indexingStatusDetail?.indexing_status || ''))
@@ -177,13 +220,17 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
return doc?.data_source_info.notion_page_icon
}
const isSourceEmbedding = (detail: IndexingStatusResponse) => ['indexing', 'splitting', 'parsing', 'cleaning', 'waiting'].includes(detail.indexing_status || '')
const isSourceEmbedding = (detail: IndexingStatusResponse) =>
['indexing', 'splitting', 'parsing', 'cleaning', 'waiting'].includes(detail.indexing_status || '')
return (
<>
<div className='h-5 flex items-center mb-5'>
<div className={s.embeddingStatus}>
{isEmbedding && t('datasetDocuments.embedding.processing')}
{isEmbedding && <div className='flex items-center'>
<RiLoader2Fill className='size-4 mr-1 animate-spin' />
{t('datasetDocuments.embedding.processing')}
</div>}
{isEmbeddingCompleted && t('datasetDocuments.embedding.completed')}
</div>
</div>
@@ -258,11 +305,19 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
</div>
))}
</div>
<RuleDetail sourceData={ruleDetail} />
<div className='flex items-center gap-2 mt-10'>
<RuleDetail sourceData={ruleDetail} indexingType={
indexingType
}
retrievalMethod={retrievalMethod}
/>
<div className='flex items-center gap-2 my-10'>
<Button className='w-fit' onClick={navToApiDocs}>
<RiTerminalBoxLine className='size-4 mr-2' />
<span>Access the API</span>
</Button>
<Button className='w-fit' variant='primary' onClick={navToDocumentList}>
<span>{t('datasetCreation.stepThree.navTo')}</span>
<ArrowRightIcon className='h-4 w-4 ml-2 stroke-current stroke-1' />
<ArrowRightIcon className='size-4 ml-2 stroke-current stroke-1' />
</Button>
</div>
</>

View File

@@ -0,0 +1,16 @@
import GoldIcon from './assets/gold.svg'
import Piggybank from './assets/piggy-bank-mod.svg'
import Selection from './assets/selection-mod.svg'
import Research from './assets/research-mod.svg'
import PatternRecognition from './assets/pattern-recognition-mod.svg'
export const indexMethodIcon = {
high_quality: GoldIcon,
economical: Piggybank,
}
export const retrievalIcon = {
vector: Selection,
fullText: Research,
hybrid: PatternRecognition,
}

View File

@@ -3,10 +3,10 @@ import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppUnavailable from '../../base/app-unavailable'
import { ModelTypeEnum } from '../../header/account-setting/model-provider-page/declarations'
import StepsNavBar from './steps-nav-bar'
import StepOne from './step-one'
import StepTwo from './step-two'
import StepThree from './step-three'
import { Topbar } from './top-bar'
import { DataSourceType } from '@/models/datasets'
import type { CrawlOptions, CrawlResultItem, DataSet, FileItem, createDocumentResponse } from '@/models/datasets'
import { fetchDataSource } from '@/service/common'
@@ -36,6 +36,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
const [dataSourceType, setDataSourceType] = useState<DataSourceType>(DataSourceType.FILE)
const [step, setStep] = useState(1)
const [indexingTypeCache, setIndexTypeCache] = useState('')
const [retrievalMethodCache, setRetrievalMethodCache] = useState('')
const [fileList, setFiles] = useState<FileItem[]>([])
const [result, setResult] = useState<createDocumentResponse | undefined>()
const [hasError, setHasError] = useState(false)
@@ -80,6 +81,9 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
const updateResultCache = (res?: createDocumentResponse) => {
setResult(res)
}
const updateRetrievalMethodCache = (method: string) => {
setRetrievalMethodCache(method)
}
const nextStep = useCallback(() => {
setStep(step + 1)
@@ -118,12 +122,10 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
return (
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
<div className="flex flex-col w-11 sm:w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
<StepsNavBar step={step} datasetId={datasetId} />
</div>
<div className="grow bg-white">
<div className={step === 1 ? 'block h-full' : 'hidden'}>
<div className='flex flex-col' style={{ height: 'calc(100vh - 56px)' }}>
<div className="grow bg-white flex flex-col max-h-full h-full">
<Topbar activeIndex={step - 1} />
<div className={step === 1 ? 'block h-full max-h-full overflow-auto' : 'hidden'}>
<StepOne
hasConnection={hasConnection}
onSetting={() => setShowAccountSettingModal({ payload: 'data-source' })}
@@ -158,6 +160,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
websiteCrawlJobId={websiteCrawlJobId}
onStepChange={changeStep}
updateIndexingTypeCache={updateIndexingTypeCache}
updateRetrievalMethodCache={updateRetrievalMethodCache}
updateResultCache={updateResultCache}
crawlOptions={crawlOptions}
/>}
@@ -165,6 +168,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
datasetId={datasetId}
datasetName={detail?.name}
indexingType={detail?.indexing_technique || indexingTypeCache}
retrievalMethod={detail?.retrieval_model?.search_method || retrievalMethodCache}
creationCache={result}
/>}
</div>

View File

@@ -7,22 +7,24 @@ import s from './index.module.css'
import cn from '@/utils/classnames'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
import AppIcon from '@/app/components/base/app-icon'
type StepThreeProps = {
datasetId?: string
datasetName?: string
indexingType?: string
retrievalMethod?: string
creationCache?: createDocumentResponse
}
const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrievalMethod }: StepThreeProps) => {
const { t } = useTranslation()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
return (
<div className='flex w-full h-full'>
<div className='flex w-full max-h-full h-full overflow-y-auto'>
<div className={'h-full w-full overflow-y-scroll px-6 sm:px-16'}>
<div className='max-w-[636px]'>
{!datasetId && (
@@ -30,8 +32,13 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: Step
<div className={s.creationInfo}>
<div className={s.title}>{t('datasetCreation.stepThree.creationTitle')}</div>
<div className={s.content}>{t('datasetCreation.stepThree.creationContent')}</div>
<div className={s.label}>{t('datasetCreation.stepThree.label')}</div>
<div className={s.datasetName}>{datasetName || creationCache?.dataset?.name}</div>
<div className='flex gap-4'>
<AppIcon {...creationCache?.dataset} className='size-14' />
<div className='w-full'>
<div className={s.label}>{t('datasetCreation.stepThree.label')}</div>
<div className={s.datasetName}>{datasetName || creationCache?.dataset?.name}</div>
</div>
</div>
</div>
<div className={s.dividerLine} />
</>
@@ -47,6 +54,7 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: Step
batchId={creationCache?.batch || ''}
documents={creationCache?.documents as FullDocumentDetail[]}
indexingType={indexingType || creationCache?.dataset?.indexing_technique}
retrievalMethod={retrievalMethod || creationCache?.dataset?.retrieval_model?.search_method}
/>
</div>
</div>

View File

@@ -146,7 +146,7 @@
}
.typeIcon.economical {
background-image: url(../assets/piggy-bank-01.svg);
background-image: url(../assets/piggy-bank-mod.svg);
}
.radioItem .radio {
@@ -432,4 +432,4 @@
font-size: 12px;
line-height: 18px;
}
}
}

View File

@@ -1,20 +1,31 @@
'use client'
import type { FC, PropsWithChildren, ReactNode } from 'react'
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useBoolean } from 'ahooks'
import { XMarkIcon } from '@heroicons/react/20/solid'
import { RocketLaunchIcon } from '@heroicons/react/24/outline'
import {
RiArrowLeftLine,
RiCloseLine,
RiSearchEyeLine,
} from '@remixicon/react'
import Link from 'next/link'
import { groupBy } from 'lodash-es'
import Image from 'next/image'
import SettingCog from '../assets/setting-gear-mod.svg'
import OrangeEffect from '../assets/option-card-effect-orange.svg'
import FamilyMod from '../assets/family-mod.svg'
import Note from '../assets/note-mod.svg'
import FileList from '../assets/file-list-3-fill.svg'
import { indexMethodIcon } from '../icons'
import PreviewItem, { PreviewType } from './preview-item'
import LanguageSelect from './language-select'
import s from './index.module.css'
import unescape from './unescape'
import escape from './escape'
import { OptionCard } from './option-card'
import LanguageSelect from './language-select'
import { DelimiterInput, MaxLengthInput, OverlapInput } from './inputs'
import cn from '@/utils/classnames'
import type { CrawlOptions, CrawlResultItem, CreateDocumentReq, CustomFile, FileIndexingEstimateResponse, FullDocumentDetail, IndexingEstimateParams, NotionInfo, PreProcessingRule, ProcessRule, Rules, createDocumentResponse } from '@/models/datasets'
import {
@@ -24,7 +35,6 @@ import {
fetchDefaultProcessRule,
} from '@/service/datasets'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import Loading from '@/app/components/base/loading'
import FloatRightContainer from '@/app/components/base/float-right-container'
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
@@ -32,25 +42,34 @@ import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/ec
import { type RetrievalConfig } from '@/types/app'
import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
import Toast from '@/app/components/base/toast'
import { formatNumber } from '@/utils/format'
import type { NotionPage } from '@/models/common'
import { DataSourceProvider } from '@/models/common'
import { DataSourceType, DocForm } from '@/models/datasets'
import NotionIcon from '@/app/components/base/notion-icon'
import Switch from '@/app/components/base/switch'
import { MessageChatSquare } from '@/app/components/base/icons/src/public/common'
import { useDatasetDetailContext } from '@/context/dataset-detail'
import I18n from '@/context/i18n'
import { IS_CE_EDITION } from '@/config'
import { RETRIEVE_METHOD } from '@/types/app'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Tooltip from '@/app/components/base/tooltip'
import { useDefaultModel, useModelList, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { LanguagesSupported } from '@/i18n/language'
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { Globe01 } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
import Checkbox from '@/app/components/base/checkbox'
import RadioCard from '@/app/components/base/radio-card'
import { MessageChatSquare } from '@/app/components/base/icons/src/public/common'
import { IS_CE_EDITION } from '@/config'
import Switch from '@/app/components/base/switch'
const TextLabel: FC<PropsWithChildren> = (props) => {
return <label className='text-text-secondary text-xs font-semibold leading-none'>{props.children}</label>
}
const FormField: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
return <div className='space-y-2 flex-1'>
<TextLabel>{props.label}</TextLabel>
{props.children}
</div>
}
type ValueOf<T> = T[keyof T]
type StepTwoProps = {
@@ -60,6 +79,7 @@ type StepTwoProps = {
onSetting: () => void
datasetId?: string
indexingType?: ValueOf<IndexingType>
retrievalMethod?: string
dataSourceType: DataSourceType
files: CustomFile[]
notionPages?: NotionPage[]
@@ -69,6 +89,7 @@ type StepTwoProps = {
websiteCrawlJobId?: string
onStepChange?: (delta: number) => void
updateIndexingTypeCache?: (type: string) => void
updateRetrievalMethodCache?: (method: string) => void
updateResultCache?: (res: createDocumentResponse) => void
onSave?: () => void
onCancel?: () => void
@@ -76,7 +97,8 @@ type StepTwoProps = {
enum SegmentType {
AUTO = 'automatic',
CUSTOM = 'custom',
GENERAL = 'general',
PARENT_CHILD = 'parent_child',
}
enum IndexingType {
QUALIFIED = 'high_quality',
@@ -85,6 +107,32 @@ enum IndexingType {
const DEFAULT_SEGMENT_IDENTIFIER = '\\n\\n'
type ParentChildConfig = {
chunkForContext: 'paragraph' | 'full_doc'
parent: {
delimiter: string
maxLength: number
}
child: {
delimiter: string
maxLength: number
}
rules: PreProcessingRule[]
}
const defaultParentChildConfig: ParentChildConfig = {
chunkForContext: 'paragraph',
parent: {
delimiter: '\\n\\n',
maxLength: 4000,
},
child: {
delimiter: '\\n\\n',
maxLength: 4000,
},
rules: [],
}
const StepTwo = ({
isSetting,
documentDetail,
@@ -104,6 +152,7 @@ const StepTwo = ({
updateResultCache,
onSave,
onCancel,
updateRetrievalMethodCache,
}: StepTwoProps) => {
const { t } = useTranslation()
const { locale } = useContext(I18n)
@@ -117,7 +166,7 @@ const StepTwo = ({
const [scrolled, setScrolled] = useState(false)
const previewScrollRef = useRef<HTMLDivElement>(null)
const [previewScrolled, setPreviewScrolled] = useState(false)
const [segmentationType, setSegmentationType] = useState<SegmentType>(SegmentType.AUTO)
const [segmentationType, setSegmentationType] = useState<SegmentType>(SegmentType.GENERAL)
const [segmentIdentifier, doSetSegmentIdentifier] = useState(DEFAULT_SEGMENT_IDENTIFIER)
const setSegmentIdentifier = useCallback((value: string) => {
doSetSegmentIdentifier(value ? escape(value) : DEFAULT_SEGMENT_IDENTIFIER)
@@ -143,14 +192,17 @@ const StepTwo = ({
const [QATipHide, setQATipHide] = useState(false)
const [previewSwitched, setPreviewSwitched] = useState(false)
const [showPreview, { setTrue: setShowPreview, setFalse: hidePreview }] = useBoolean()
const [customFileIndexingEstimate, setCustomFileIndexingEstimate] = useState<FileIndexingEstimateResponse | null>(null)
const [generalFileIndexingEstimate, setGeneralFileIndexingEstimate] = useState<FileIndexingEstimateResponse | null>(null)
const [automaticFileIndexingEstimate, setAutomaticFileIndexingEstimate] = useState<FileIndexingEstimateResponse | null>(null)
// TODO: refactor this
const fileIndexingEstimate = (() => {
return segmentationType === SegmentType.AUTO ? automaticFileIndexingEstimate : customFileIndexingEstimate
return segmentationType === SegmentType.AUTO ? automaticFileIndexingEstimate : generalFileIndexingEstimate
})()
const [isCreating, setIsCreating] = useState(false)
const [parentChildConfig, setParentChildConfig] = useState<ParentChildConfig>(defaultParentChildConfig)
const scrollHandle = (e: Event) => {
if ((e.target as HTMLDivElement).scrollTop > 0)
setScrolled(true)
@@ -197,26 +249,27 @@ const StepTwo = ({
if (defaultConfig) {
setSegmentIdentifier(defaultConfig.segmentation.separator)
setMax(defaultConfig.segmentation.max_tokens)
setOverlap(defaultConfig.segmentation.chunk_overlap)
setOverlap(defaultConfig.segmentation.chunk_overlap!)
setRules(defaultConfig.pre_processing_rules)
}
setParentChildConfig(defaultParentChildConfig)
}
const fetchFileIndexingEstimate = async (docForm = DocForm.TEXT, language?: string) => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const res = await didFetchFileIndexingEstimate(getFileIndexingEstimateParams(docForm, language)!)
if (segmentationType === SegmentType.CUSTOM)
setCustomFileIndexingEstimate(res)
if (segmentationType === SegmentType.GENERAL)
setGeneralFileIndexingEstimate(res)
else
setAutomaticFileIndexingEstimate(res)
}
const confirmChangeCustomConfig = () => {
if (segmentationType === SegmentType.CUSTOM && max > 4000) {
if (segmentationType === SegmentType.GENERAL && max > 4000) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck') })
return
}
setCustomFileIndexingEstimate(null)
setGeneralFileIndexingEstimate(null)
setShowPreview()
fetchFileIndexingEstimate()
setPreviewSwitched(false)
@@ -229,7 +282,7 @@ const StepTwo = ({
rules: {} as any, // api will check this. It will be removed after api refactored.
mode: segmentationType,
}
if (segmentationType === SegmentType.CUSTOM) {
if (segmentationType === SegmentType.GENERAL) {
const ruleObj = {
pre_processing_rules: rules,
segmentation: {
@@ -339,11 +392,11 @@ const StepTwo = ({
)
const getCreationParams = () => {
let params
if (segmentationType === SegmentType.CUSTOM && overlap > max) {
if (segmentationType === SegmentType.GENERAL && overlap > max) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.overlapCheck') })
return
}
if (segmentationType === SegmentType.CUSTOM && max > 4000) {
if (segmentationType === SegmentType.GENERAL && max > 4000) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck') })
return
}
@@ -461,6 +514,8 @@ const StepTwo = ({
})
updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
updateResultCache && updateResultCache(res)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string)
}
else {
res = await createDocument({
@@ -496,10 +551,8 @@ const StepTwo = ({
const previewSwitch = async (language?: string) => {
setPreviewSwitched(true)
setIsLanguageSelectDisabled(true)
if (segmentationType === SegmentType.AUTO)
setAutomaticFileIndexingEstimate(null)
else
setCustomFileIndexingEstimate(null)
if (segmentationType === SegmentType.GENERAL)
setGeneralFileIndexingEstimate(null)
try {
await fetchFileIndexingEstimate(DocForm.QA, language)
}
@@ -564,17 +617,9 @@ const StepTwo = ({
}, [isAPIKeySet, indexingType, datasetId])
useEffect(() => {
if (segmentationType === SegmentType.AUTO) {
setAutomaticFileIndexingEstimate(null)
!isMobile && setShowPreview()
fetchFileIndexingEstimate()
setPreviewSwitched(false)
}
else {
hidePreview()
setCustomFileIndexingEstimate(null)
setPreviewSwitched(false)
}
hidePreview()
setGeneralFileIndexingEstimate(null)
setPreviewSwitched(false)
}, [segmentationType, indexType])
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict || {
@@ -590,132 +635,188 @@ const StepTwo = ({
} as RetrievalConfig)
return (
<div className='flex w-full h-full'>
<div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
<div className={cn(s.pageHeader, scrolled && s.fixed, isMobile && '!px-6')}>
<span>{t('datasetCreation.steps.two')}</span>
{(isMobile || !showPreview) && (
<Button
className='border-[0.5px] !h-8 hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]'
onClick={setShowPreview}
>
<Tooltip>
<div className="flex flex-row items-center">
<RocketLaunchIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
<span className="text-[13px]">{t('datasetCreation.stepTwo.previewTitleButton')}</span>
</div>
</Tooltip>
</Button>
)}
</div>
<div className='flex w-full max-h-full h-full overflow-y-auto'>
<div className='relative h-full w-full overflow-y-scroll'>
<div className={cn(s.form, isMobile && '!px-4')}>
<div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
<div className='max-w-[640px]'>
<div
className={cn(
s.radioItem,
s.segmentationItem,
segmentationType === SegmentType.AUTO && s.active,
)}
onClick={() => setSegmentationType(SegmentType.AUTO)}
>
<span className={cn(s.typeIcon, s.auto)} />
<span className={cn(s.radio)} />
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.auto')}</div>
<div className={s.tip}>{t('datasetCreation.stepTwo.autoDescription')}</div>
</div>
</div>
<div
className={cn(
s.radioItem,
s.segmentationItem,
segmentationType === SegmentType.CUSTOM && s.active,
segmentationType === SegmentType.CUSTOM && s.custom,
)}
onClick={() => setSegmentationType(SegmentType.CUSTOM)}
>
<span className={cn(s.typeIcon, s.customize)} />
<span className={cn(s.radio)} />
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.custom')}</div>
<div className={s.tip}>{t('datasetCreation.stepTwo.customDescription')}</div>
</div>
{segmentationType === SegmentType.CUSTOM && (
<div className={s.typeFormBody}>
<div className={s.formRow}>
<div className='w-full'>
<div className={s.label}>
{t('datasetCreation.stepTwo.separator')}
<Tooltip
popupContent={
<div className='max-w-[200px]'>
{t('datasetCreation.stepTwo.separatorTip')}
</div>
}
/>
<div className='space-y-4'>
<OptionCard
title={t('datasetCreation.stepTwo.general')}
icon={<Image src={SettingCog} alt={t('datasetCreation.stepTwo.general')} />}
activeHeaderClassName='bg-gradient-to-r from-[#EFF0F9] to-[#F9FAFB]'
description={t('datasetCreation.stepTwo.generalTip')}
isActive={SegmentType.GENERAL === segmentationType}
onClick={() => setSegmentationType(SegmentType.GENERAL)}
actions={
<>
<Button variant={'secondary-accent'}>
<RiSearchEyeLine className='h-4 w-4 mr-1.5' />
{t('datasetCreation.stepTwo.previewChunk')}
</Button>
<Button variant={'ghost'} onClick={resetRules}>
{t('datasetCreation.stepTwo.reset')}
</Button>
</>
}
>
<div className='space-y-4'>
<div className='flex gap-3'>
<DelimiterInput
value={segmentIdentifier}
onChange={e => setSegmentIdentifier(e.target.value)}
/>
<MaxLengthInput
value={max}
onChange={setMax}
/>
<OverlapInput
value={overlap}
min={1}
onChange={setOverlap}
/>
</div>
<div className='space-y-2'>
<div className='w-full flex flex-col'>
<TextLabel>{t('datasetCreation.stepTwo.rules')}</TextLabel>
<div className='mt-4 space-y-2'>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem} onClick={() => {
ruleChangeHandle(rule.id)
}}>
<Checkbox
checked={rule.enabled}
/>
<label className="ml-2 text-sm font-normal cursor-pointer text-gray-800">{getRuleName(rule.id)}</label>
</div>
))}
</div>
<Input
type="text"
className='h-9'
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={segmentIdentifier}
onChange={e => setSegmentIdentifier(e.target.value)}
/>
</div>
</div>
<div className={s.formRow}>
<div className='w-full'>
<div className={s.label}>{t('datasetCreation.stepTwo.maxLength')}</div>
<Input
type="number"
className='h-9'
placeholder={t('datasetCreation.stepTwo.maxLength') || ''}
value={max}
max={4000}
min={1}
onChange={e => setMax(parseInt(e.target.value.replace(/^0+/, ''), 10))}
/>
</div>
</div>
<div className={s.formRow}>
<div className='w-full'>
<div className={s.label}>
{t('datasetCreation.stepTwo.overlap')}
<Tooltip
popupContent={
<div className='max-w-[200px]'>
{t('datasetCreation.stepTwo.overlapTip')}
</div>
}
/>
</div>
<Input
type="number"
className='h-9'
placeholder={t('datasetCreation.stepTwo.overlap') || ''}
value={overlap}
min={1}
onChange={e => setOverlap(parseInt(e.target.value.replace(/^0+/, ''), 10))}
/>
</div>
</div>
<div className={s.formRow}>
<div className='w-full flex flex-col gap-1'>
<div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem}>
<input id={rule.id} type="checkbox" checked={rule.enabled} onChange={() => ruleChangeHandle(rule.id)} className="w-4 h-4 rounded border-gray-300 text-blue-700 focus:ring-blue-700" />
<label htmlFor={rule.id} className="ml-2 text-sm font-normal cursor-pointer text-gray-800">{getRuleName(rule.id)}</label>
</div>
))}
</div>
</div>
<div className={s.formFooter}>
<Button variant="primary" className={cn(s.button)} onClick={confirmChangeCustomConfig}>{t('datasetCreation.stepTwo.preview')}</Button>
<Button className={cn(s.button, 'ml-2')} onClick={resetRules}>{t('datasetCreation.stepTwo.reset')}</Button>
</div>
</div>
)}
</OptionCard>
<OptionCard
title={t('datasetCreation.stepTwo.parentChild')}
icon={<Image src={FamilyMod} alt={t('datasetCreation.stepTwo.parentChild')} />}
effectImg={OrangeEffect.src}
activeHeaderClassName='bg-gradient-to-r from-[#F9F1EE] to-[#F9FAFB]'
description={t('datasetCreation.stepTwo.parentChildTip')}
isActive={SegmentType.PARENT_CHILD === segmentationType}
onClick={() => setSegmentationType(SegmentType.PARENT_CHILD)}
actions={
<>
<Button variant={'secondary-accent'}>
<RiSearchEyeLine className='h-4 w-4 mr-1.5' />
{t('datasetCreation.stepTwo.previewChunk')}
</Button>
<Button variant={'ghost'} onClick={resetRules}>
{t('datasetCreation.stepTwo.reset')}
</Button>
</>
}
>
<div className='space-y-4'>
<div className='space-y-2'>
<TextLabel>
{t('datasetCreation.stepTwo.parentChunkForContext')}
</TextLabel>
<RadioCard
icon={<Image src={Note} alt='' />}
title={t('datasetCreation.stepTwo.paragraph')}
description={t('datasetCreation.stepTwo.paragraphTip')}
isChosen={parentChildConfig.chunkForContext === 'paragraph'}
onChosen={() => setParentChildConfig(
{
...parentChildConfig,
chunkForContext: 'paragraph',
},
)}
chosenConfig={
<div className='flex gap-2'>
<DelimiterInput
value={parentChildConfig.parent.delimiter}
onChange={e => setParentChildConfig({
...parentChildConfig,
parent: {
...parentChildConfig.parent,
delimiter: e.target.value,
},
})}
/>
<MaxLengthInput
value={parentChildConfig.parent.maxLength}
onChange={value => setParentChildConfig({
...parentChildConfig,
parent: {
...parentChildConfig.parent,
maxLength: value,
},
})}
/>
</div>
}
/>
<RadioCard
icon={<Image src={FileList} alt='' />}
title={t('datasetCreation.stepTwo.fullDoc')}
description={t('datasetCreation.stepTwo.fullDocTip')}
onChosen={() => setParentChildConfig(
{
...parentChildConfig,
chunkForContext: 'full_doc',
},
)}
isChosen={parentChildConfig.chunkForContext === 'full_doc'}
/>
</div>
<div className='space-y-4'>
<TextLabel>
{t('datasetCreation.stepTwo.childChunkForRetrieval')}
</TextLabel>
<div className='flex gap-3 mt-2'>
<DelimiterInput
value={parentChildConfig.child.delimiter}
onChange={e => setParentChildConfig({
...parentChildConfig,
child: {
...parentChildConfig.child,
delimiter: e.target.value,
},
})}
/>
<MaxLengthInput
value={parentChildConfig.child.maxLength}
onChange={value => setParentChildConfig({
...parentChildConfig,
child: {
...parentChildConfig.child,
maxLength: value,
},
})}
/>
</div>
<div className='space-y-2'>
<TextLabel>
{t('datasetCreation.stepTwo.rules')}
</TextLabel>
<div className='space-y-2 mt-2'>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem} onClick={() => {
ruleChangeHandle(rule.id)
}}>
<Checkbox
checked={rule.enabled}
/>
<label className="ml-2 text-sm font-normal cursor-pointer text-gray-800">{getRuleName(rule.id)}</label>
</div>
))}
</div>
</div>
</div>
</div>
</OptionCard>
</div>
</div>
<div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
@@ -736,7 +837,9 @@ const StepTwo = ({
setIndexType(IndexingType.QUALIFIED)
}}
>
<span className={cn(s.typeIcon, s.qualified)} />
<div className='h-8 p-1.5 bg-white rounded-lg border border-components-panel-border-subtle justify-center items-center inline-flex absolute left-5 top-[18px]'>
<Image src={indexMethodIcon.high_quality} alt='Gold Icon' width={20} height={20} />
</div>
{!hasSetIndexType && <span className={cn(s.radio)} />}
<div className={s.typeHeader}>
<div className={s.title}>
@@ -765,7 +868,9 @@ const StepTwo = ({
)}
onClick={changeToEconomicalType}
>
<span className={cn(s.typeIcon, s.economical)} />
<div className='h-8 p-1.5 bg-white rounded-lg border border-components-panel-border-subtle justify-center items-center inline-flex absolute left-5 top-[18px]'>
<Image src={indexMethodIcon.economical} alt='Economical Icon' width={20} height={20} />
</div>
{!hasSetIndexType && <span className={cn(s.radio)} />}
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.economical')}</div>
@@ -777,7 +882,7 @@ const StepTwo = ({
{hasSetIndexType && indexType === IndexingType.ECONOMICAL && (
<div className='mt-2 text-xs text-gray-500 font-medium'>
{t('datasetCreation.stepTwo.indexSettingTip')}
<Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
<Link className='text-text-accent' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
</div>
)}
{IS_CE_EDITION && indexType === IndexingType.QUALIFIED && (
@@ -787,19 +892,17 @@ const StepTwo = ({
<MessageChatSquare className='w-4 h-4' />
</div>
<div className='grow mx-3'>
<div className='mb-[2px] text-md font-medium text-gray-900'>{t('datasetCreation.stepTwo.QATitle')}</div>
<div className='mb-0.5 text-md font-medium text-gray-900'>{t('datasetCreation.stepTwo.QATitle')}</div>
<div className='inline-flex items-center text-[13px] leading-[18px] text-gray-500'>
<span className='pr-1'>{t('datasetCreation.stepTwo.QALanguage')}</span>
<LanguageSelect currentLanguage={docLanguage} onSelect={handleSelect} disabled={isLanguageSelectDisabled} />
</div>
</div>
<div className='shrink-0'>
<Switch
defaultValue={docForm === DocForm.QA}
onChange={handleSwitch}
size='md'
/>
</div>
<Switch
defaultValue={docForm === DocForm.QA}
onChange={handleSwitch}
size='md'
/>
</div>
{docForm === DocForm.QA && !QATipHide && (
<div className='flex justify-between items-center px-5 py-2 bg-orange-50 border-t border-amber-100 rounded-b-xl text-[13px] leading-[18px] text-medium text-amber-500'>
@@ -824,7 +927,7 @@ const StepTwo = ({
{!!datasetId && (
<div className='mt-2 text-xs text-gray-500 font-medium'>
{t('datasetCreation.stepTwo.indexSettingTip')}
<Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
<Link className='text-text-accent' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
</div>
)}
</div>
@@ -836,7 +939,7 @@ const StepTwo = ({
<div className={s.label}>
<div className='shrink-0 mr-4'>{t('datasetSettings.form.retrievalSetting.title')}</div>
<div className='leading-[18px] text-xs font-normal text-gray-500'>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
{t('datasetSettings.form.retrievalSetting.longDescription')}
</div>
</div>
@@ -866,83 +969,14 @@ const StepTwo = ({
</div>
</div>
<div className={s.source}>
<div className={s.sourceContent}>
{dataSourceType === DataSourceType.FILE && (
<>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.fileSource')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
<span className={cn(s.fileIcon, files.length && s[files[0].extension || ''])} />
{getFileName(files[0].name || '')}
{files.length > 1 && (
<span className={s.sourceCount}>
<span>{t('datasetCreation.stepTwo.other')}</span>
<span>{files.length - 1}</span>
<span>{t('datasetCreation.stepTwo.fileUnit')}</span>
</span>
)}
</div>
</>
)}
{dataSourceType === DataSourceType.NOTION && (
<>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.notionSource')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
<NotionIcon
className='shrink-0 mr-1'
type='page'
src={notionPages[0]?.page_icon}
/>
{notionPages[0]?.page_name}
{notionPages.length > 1 && (
<span className={s.sourceCount}>
<span>{t('datasetCreation.stepTwo.other')}</span>
<span>{notionPages.length - 1}</span>
<span>{t('datasetCreation.stepTwo.notionUnit')}</span>
</span>
)}
</div>
</>
)}
{dataSourceType === DataSourceType.WEB && (
<>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.websiteSource')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
<Globe01 className='shrink-0 mr-1' />
<span className='grow w-0 truncate'>{websitePages[0].source_url}</span>
{websitePages.length > 1 && (
<span className={s.sourceCount}>
<span>{t('datasetCreation.stepTwo.other')}</span>
<span>{websitePages.length - 1}</span>
<span>{t('datasetCreation.stepTwo.webpageUnit')}</span>
</span>
)}
</div>
</>
)}
</div>
<div className={s.divider} />
<div className={s.segmentCount}>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.estimateSegment')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
{
fileIndexingEstimate
? (
<div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.total_segments)} </div>
)
: (
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div>
)
}
</div>
</div>
</div>
{!isSetting
? (
<div className='flex items-center mt-8 py-2'>
<Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.previousStep')}</Button>
<div className={s.divider} />
<Button loading={isCreating} variant='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
<Button onClick={() => onStepChange && onStepChange(-1)}>
<RiArrowLeftLine className='w-4 h-4 mr-1' />
{t('datasetCreation.stepTwo.previousStep')}
</Button>
<Button className='ml-auto' loading={isCreating} variant='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
</div>
)
: (

View File

@@ -0,0 +1,77 @@
import type { FC, PropsWithChildren, ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import type { InputProps } from '@/app/components/base/input'
import Input from '@/app/components/base/input'
import Tooltip from '@/app/components/base/tooltip'
import type { InputNumberProps } from '@/app/components/base/input-number'
import { InputNumber } from '@/app/components/base/input-number'
const TextLabel: FC<PropsWithChildren> = (props) => {
return <label className='text-text-secondary text-xs font-semibold leading-none'>{props.children}</label>
}
const FormField: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
return <div className='space-y-2 flex-1'>
<TextLabel>{props.label}</TextLabel>
{props.children}
</div>
}
export const DelimiterInput: FC<InputProps> = (props) => {
const { t } = useTranslation()
return <FormField label={<div className='flex'>
{t('datasetCreation.stepTwo.separator')}
<Tooltip
popupContent={
<div className='max-w-[200px]'>
{t('datasetCreation.stepTwo.separatorTip')}
</div>
}
/>
</div>}>
<Input
type="text"
className='h-9'
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''}
{...props}
/>
</FormField>
}
export const MaxLengthInput: FC<InputNumberProps> = (props) => {
const { t } = useTranslation()
return <FormField label={<div className='h-[14px]'>
{t('datasetCreation.stepTwo.maxLength')}
</div>}>
<InputNumber
type="number"
className='h-9'
placeholder={t('datasetCreation.stepTwo.maxLength') || ''}
max={4000}
min={1}
{...props}
/>
</FormField>
}
export const OverlapInput: FC<InputNumberProps> = (props) => {
const { t } = useTranslation()
return <FormField label={<div className='flex'>
{t('datasetCreation.stepTwo.overlap')}
<Tooltip
popupContent={
<div className='max-w-[200px]'>
{t('datasetCreation.stepTwo.overlapTip')}
</div>
}
/>
</div>}>
<InputNumber
type="number"
className='h-9'
placeholder={t('datasetCreation.stepTwo.overlap') || ''}
min={1}
{...props}
/>
</FormField>
}

View File

@@ -0,0 +1,83 @@
import { type ComponentProps, type FC, type ReactNode } from 'react'
import Image from 'next/image'
import piggyBank from '../assets/piggy-bank-01.svg'
import Effect from '../assets/option-card-effect-blue.svg'
import classNames from '@/utils/classnames'
const TriangleArrow: FC<ComponentProps<'svg'>> = props => (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="11" viewBox="0 0 24 11" fill="none" {...props}>
<path d="M9.87868 1.12132C11.0503 -0.0502525 12.9497 -0.0502525 14.1213 1.12132L23.3137 10.3137H0.686292L9.87868 1.12132Z" fill="white"/>
</svg>
)
type OptionCardHeaderProps = {
icon: ReactNode
title: ReactNode
description: string
isActive?: boolean
activeClassName?: string
effectImg?: string
}
export const OptionCardHeader: FC<OptionCardHeaderProps> = (props) => {
const { icon, title, description, isActive, activeClassName, effectImg } = props
return <div className={classNames(
'flex h-full overflow-hidden relative',
isActive && activeClassName,
)}>
<div className='size-14 flex items-center justify-center relative overflow-hidden'>
{isActive && <Image src={effectImg || Effect.src} className='absolute top-0 left-0 w-full h-full' alt='' width={56} height={56} />}
<div className='size-8 rounded-lg border p-1.5 shadow border-components-panel-border-subtle justify-center flex bg-white'>
{icon || <Image src={piggyBank.src} className='size-5' alt={description} width={20} height={20} />}
</div>
</div>
<TriangleArrow
className='absolute left-4 -bottom-1.5'
/>
<div className='flex-1 space-y-1 py-3 pr-4'>
<div className='text-text-secondary text-sm font-semibold leading-tight'>{title}</div>
<div className='text-text-tertiary text-xs font-normal leading-none'>{description}</div>
</div>
</div>
}
type OptionCardProps = {
icon: ReactNode
className?: string
activeHeaderClassName?: string
title: ReactNode
description: string
isActive?: boolean
actions?: ReactNode
effectImg?: string
} & Omit<ComponentProps<'div'>, 'title'>
export const OptionCard: FC<OptionCardProps> = (props) => {
const { icon, className, title, description, isActive, children, actions, activeHeaderClassName, style, effectImg, ...rest } = props
return <div
className={classNames(
'rounded-xl overflow-hidden',
isActive ? 'border-components-option-card-option-selected-border bg-components-panel-bg' : 'border-components-option-card-option-border bg-components-option-card-option-bg',
className,
)}
style={{
...style,
borderWidth: 1.5,
}}
{...rest}>
<OptionCardHeader
icon={icon}
title={title}
description={description}
isActive={isActive}
activeClassName={activeHeaderClassName}
effectImg={effectImg}
/>
{/** Body */}
{isActive && <div className='p-3'>{children}
{actions && <div className='flex gap-2 mt-4'>
{actions}
</div>}
</div>}
</div>
}

View File

@@ -0,0 +1,34 @@
import type { FC } from 'react'
import type { Step } from './step'
import { StepperStep } from './step'
export type StepperProps = {
steps: Step[]
activeIndex: number
}
function join<T, R = T>(array: T[], sep: R): Array<T | R> {
return array.reduce((acc, item, index) => {
if (index === 0)
return [item]
return acc.concat([sep, item])
}, [] as Array<T | R>)
}
export const Stepper: FC<StepperProps> = (props) => {
const { steps, activeIndex } = props
return <div className='flex items-center gap-3'>
{join(
steps.map((step, index) => (
<StepperStep
key={index}
{...step}
activeIndex={activeIndex}
index={index}
/>
)),
<div className="w-4 h-px bg-text-quaternary" />,
)}
</div>
}

View File

@@ -0,0 +1,47 @@
import type { FC } from 'react'
import classNames from '@/utils/classnames'
export type Step = {
name: string
}
export type StepperStepProps = Step & {
index: number
activeIndex: number
}
export const StepperStep: FC<StepperStepProps> = (props) => {
const { name, activeIndex, index } = props
const isActive = index === activeIndex
const isDisabled = activeIndex < index
const label = isActive ? `STEP ${index + 1}` : `${index + 1}`
return <div className='flex items-center gap-2'>
<div className={classNames(
'h-5 px-2 py-1 rounded-3xl flex-col justify-center items-center gap-2 inline-flex',
isActive
? 'bg-state-accent-solid'
: !isDisabled
? 'border border-text-tertiary'
: 'border border-divider-deep',
)}>
<div className={classNames(
'text-center text-[10px] font-semibold uppercase leading-3',
isActive
? 'text-text-primary-on-surface'
: !isDisabled
? 'text-text-tertiary'
: 'text-text-tertiary opacity-30',
)}>
{label}
</div>
</div>
<div className={classNames(
' text-xs font-medium uppercase leading-none',
isActive
? 'text-text-accent'
: !isDisabled
? 'text-text-tertiary'
: 'text-text-tertiary opacity-30',
)}>{name}</div>
</div>
}

View File

@@ -0,0 +1,39 @@
import type { FC } from 'react'
import { RiArrowLeftLine } from '@remixicon/react'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import { Stepper, type StepperProps } from '../stepper'
import classNames from '@/utils/classnames'
export type TopbarProps = Pick<StepperProps, 'activeIndex'> & {
className?: string
}
const STEP_T_MAP: Record<number, string> = {
1: 'datasetCreation.steps.one',
2: 'datasetCreation.steps.two',
3: 'datasetCreation.steps.three',
}
export const Topbar: FC<TopbarProps> = (props) => {
const { className, ...rest } = props
const { t } = useTranslation()
return <div className={classNames('flex items-center justify-between relative', className)}>
<Link href={'/datasets'} className="h-12 pl-2 pr-6 py-2 justify-start items-center gap-1 inline-flex">
<RiArrowLeftLine className='size-4 mr-2' />
<p className="text-text-primary text-[13px] font-semibold uppercase leading-none">
{t('datasetCreation.steps.header.creation')}
</p>
</Link>
<div className={
'top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 absolute'
}>
<Stepper
steps={Array.from({ length: 3 }, (_, i) => ({
name: t(STEP_T_MAP[i + 1]),
}))}
{...rest}
/>
</div>
</div>
}

View File

@@ -18,7 +18,7 @@ const ErrorMessage: FC<Props> = ({
return (
<div className={cn(className, 'py-2 px-4 border-t border-gray-200 bg-[#FFFAEB]')}>
<div className='flex items-center h-5'>
<AlertTriangle className='mr-2 w-4 h-4 text-[#F79009]' />
<AlertTriangle className='mr-2 w-4 h-4 text-text-warning-secondary' />
<div className='text-sm font-medium text-[#DC6803]'>{title}</div>
</div>
{errorMsg && (

View File

@@ -97,7 +97,7 @@ const CSVDownload: FC<{ docForm: DocForm }> = ({ docForm }) => {
bom={true}
data={getTemplate()}
>
<div className='flex items-center h-[18px] space-x-1 text-[#155EEF] text-xs font-medium'>
<div className='flex items-center h-[18px] space-x-1 text-text-accent text-xs font-medium'>
<DownloadIcon className='w-3 h-3 mr-1' />
{t('datasetDocuments.list.batchModal.template')}
</div>

View File

@@ -6,7 +6,7 @@ import {
RiDeleteBinLine,
} from '@remixicon/react'
import { StatusItem } from '../../list'
import { DocumentTitle } from '../index'
import style from '../../style.module.css'
import s from './style.module.css'
import { SegmentIndexTag } from './index'
import cn from '@/utils/classnames'
@@ -31,6 +31,22 @@ const ProgressBar: FC<{ percent: number; loading: boolean }> = ({ percent, loadi
)
}
type DocumentTitleProps = {
extension?: string
name?: string
iconCls?: string
textCls?: string
wrapperCls?: string
}
const DocumentTitle: FC<DocumentTitleProps> = ({ extension, name, iconCls, textCls, wrapperCls }) => {
const localExtension = extension?.toLowerCase() || name?.split('.')?.pop()?.toLowerCase()
return <div className={cn('flex items-center justify-start flex-1', wrapperCls)}>
<div className={cn(s[`${localExtension || 'txt'}Icon`], style.titleIcon, iconCls)}></div>
<span className={cn('font-semibold text-lg text-gray-900 ml-1', textCls)}> {name || '--'}</span>
</div>
}
export type UsageScene = 'doc' | 'hitTesting'
type ISegmentCardProps = {

View File

@@ -2,11 +2,11 @@
import type { FC } from 'react'
import React, { useState } from 'react'
import useSWR from 'swr'
import { ArrowLeftIcon } from '@heroicons/react/24/solid'
import { createContext, useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { omit } from 'lodash-es'
import { RiArrowDownSLine, RiArrowLeftLine, RiLayoutRight2Line } from '@remixicon/react'
import { OperationAction, StatusItem } from '../list'
import s from '../style.module.css'
import Completed from './completed'
@@ -21,7 +21,7 @@ import Loading from '@/app/components/base/loading'
import type { MetadataType } from '@/service/datasets'
import { checkSegmentBatchImportProgress, fetchDocumentDetail, segmentBatchImport } from '@/service/datasets'
import { ToastContext } from '@/app/components/base/toast'
import type { DocForm } from '@/models/datasets'
import type { DocForm, ParentMode, ProcessMode } from '@/models/datasets'
import { useDatasetDetailContext } from '@/context/dataset-detail'
import FloatRightContainer from '@/app/components/base/float-right-container'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
@@ -31,17 +31,36 @@ export const DocumentContext = createContext<{ datasetId?: string; documentId?:
type DocumentTitleProps = {
extension?: string
name?: string
processMode?: ProcessMode
parent_mode?: ParentMode
iconCls?: string
textCls?: string
wrapperCls?: string
}
export const DocumentTitle: FC<DocumentTitleProps> = ({ extension, name, iconCls, textCls, wrapperCls }) => {
export const DocumentTitle: FC<DocumentTitleProps> = ({ extension, name, processMode, parent_mode, iconCls, textCls, wrapperCls }) => {
const localExtension = extension?.toLowerCase() || name?.split('.')?.pop()?.toLowerCase()
return <div className={cn('flex items-center justify-start flex-1', wrapperCls)}>
<div className={cn(s[`${localExtension || 'txt'}Icon`], style.titleIcon, iconCls)}></div>
<span className={cn('font-semibold text-lg text-gray-900 ml-1', textCls)}> {name || '--'}</span>
</div>
return (
<div className={cn('flex items-center justify-start flex-1 cursor-pointer', wrapperCls)}>
{/* // todo: add file switcher */}
<div className='flex items-center ml-1 px-2 py-0.5 rounded-lg hover:bg-state-base-hover'>
{/* // todo: add icons map */}
<div className={cn(s[`${localExtension || 'txt'}Icon`], style.titleIcon, iconCls)}></div>
<div className='flex flex-col items-start ml-1 mr-0.5'>
<div className='flex items-center'>
<span className={cn('system-md-semibold', textCls)}> {name || '--'}</span>
<RiArrowDownSLine className='h-4 w-4 text-text-primary'/>
</div>
<div className='flex items-center gap-x-0.5'>
<div className='w-3 h-3 bg-text-tertiary'/>
<span className={'system-2xs-medium-uppercase'}>
{`${processMode || '--'}${processMode === 'hierarchical' ? `·${parent_mode || '--'}` : ''}`}
</span>
</div>
</div>
</div>
</div>
)
}
type Props = {
@@ -130,14 +149,17 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
return (
<DocumentContext.Provider value={{ datasetId, documentId, docForm: documentDetail?.doc_form || '' }}>
<div className='flex flex-col h-full'>
<div className='flex min-h-16 border-b-gray-100 border-b items-center p-4 justify-between flex-wrap gap-y-2'>
<div onClick={backToPrev} className={'shrink-0 rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}>
<ArrowLeftIcon className='text-primary-600 fill-current stroke-current h-4 w-4' />
<div className='flex items-center justify-between flex-wrap min-h-16 pl-3 pr-4 py-2.5 border-b border-b-divider-subtle'>
<div onClick={backToPrev} className={'shrink-0 rounded-full w-8 h-8 flex justify-center items-center cursor-pointer hover:bg-components-button-tertiary-bg'}>
<RiArrowLeftLine className='text-components-button-ghost-text hover:text-text-tertiary w-4 h-4' />
</div>
<Divider className='!h-4' type='vertical' />
<DocumentTitle extension={documentDetail?.data_source_info?.upload_file?.extension} name={documentDetail?.name} />
<div className='flex items-center flex-wrap gap-y-2'>
<StatusItem status={documentDetail?.display_status || 'available'} scene='detail' errorMessage={documentDetail?.error || ''} />
<DocumentTitle
extension={documentDetail?.data_source_info?.upload_file?.extension}
name={documentDetail?.name}
wrapperCls='mr-2'
processMode={documentDetail?.dataset_process_rule?.mode}
/>
<div className='flex items-center flex-wrap'>
{embeddingAvailable && documentDetail && !documentDetail.archived && (
<SegmentAdd
importStatus={importStatus}
@@ -146,6 +168,20 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
showBatchModal={showBatchModal}
/>
)}
<Divider type='vertical' className='!bg-divider-regular !h-[14px] !mx-3'/>
<StatusItem
status={documentDetail?.display_status || 'available'}
scene='detail'
errorMessage={documentDetail?.error || ''}
textCls='font-semibold text-xs uppercase'
detail={{
enabled: documentDetail?.enabled || false,
archived: documentDetail?.archived || false,
id: documentId,
}}
datasetId={datasetId}
onUpdate={handleOperate}
/>
<OperationAction
scene='detail'
embeddingAvailable={embeddingAvailable}
@@ -159,12 +195,14 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
}}
datasetId={datasetId}
onUpdate={handleOperate}
className='!w-[216px]'
className='!w-[200px]'
/>
<button
className={cn(style.layoutRightIcon, showMetadata ? style.iconShow : style.iconClose)}
className={style.layoutRightIcon}
onClick={() => setShowMetadata(!showMetadata)}
/>
>
<RiLayoutRight2Line className={cn('w-4 h-4', showMetadata ? 'text-components-button-secondary-accent-text' : 'text-components-button-secondary-text')} />
</button>
</div>
</div>
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>

View File

@@ -1,5 +1,5 @@
'use client'
import type { FC } from 'react'
import type { FC, ReactNode } from 'react'
import React, { useEffect, useState } from 'react'
import { PencilIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
@@ -24,6 +24,7 @@ import type { DocType, FullDocumentDetail } from '@/models/datasets'
import { CUSTOMIZABLE_DOC_TYPES } from '@/models/datasets'
import type { inputType, metadataType } from '@/hooks/use-metadata'
import { useBookCategories, useBusinessDocCategories, useLanguages, useMetadataMap, usePersonalDocCategories } from '@/hooks/use-metadata'
import classNames from '@/utils/classnames'
const map2Options = (map: { [key: string]: string }) => {
return Object.keys(map).map(key => ({ value: key, name: map[key] }))
@@ -32,6 +33,7 @@ const map2Options = (map: { [key: string]: string }) => {
type IFieldInfoProps = {
label: string
value?: string
valueIcon?: ReactNode
displayedValue?: string
defaultValue?: string
showEdit?: boolean
@@ -43,6 +45,7 @@ type IFieldInfoProps = {
export const FieldInfo: FC<IFieldInfoProps> = ({
label,
value = '',
valueIcon,
displayedValue = '',
defaultValue,
showEdit = false,
@@ -58,7 +61,8 @@ export const FieldInfo: FC<IFieldInfoProps> = ({
return (
<div className={cn(s.fieldInfo, editAlignTop && '!items-start', readAlignTop && '!items-start pt-1')}>
<div className={cn(s.label, editAlignTop && 'pt-1')}>{label}</div>
<div className={s.value}>
<div className={classNames(s.value, 'flex items-center gap-1')}>
{valueIcon}
{!showEdit
? displayedValue
: inputType === 'select'
@@ -348,7 +352,7 @@ const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
·
<div
onClick={() => { setShowDocTypes(true) }}
className='cursor-pointer hover:text-[#155EEF]'
className='cursor-pointer hover:text-text-accent'
>
{t('common.operation.change')}
</div>

View File

@@ -3,11 +3,12 @@ import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
RiAddLine,
RiArrowDownSLine,
RiErrorWarningFill,
RiLoader2Line,
} from '@remixicon/react'
import cn from '@/utils/classnames'
import { FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general'
import Popover from '@/app/components/base/popover'
@@ -25,6 +26,7 @@ export enum ProcessStatus {
ERROR = 'error',
}
// todo: Modify processing status
const SegmentAdd: FC<ISegmentAddProps> = ({
importStatus,
clearProcessStatus,
@@ -48,14 +50,14 @@ const SegmentAdd: FC<ISegmentAddProps> = ({
<div className='inline-flex items-center mr-2 px-3 py-[6px] text-gray-700 bg-[#F6FEF9] rounded-lg border border-black/5'>
<CheckCircle className='mr-2 w-4 h-4 text-[#039855]' />
<span className='font-medium text-[13px] leading-[18px]'>{t('datasetDocuments.list.batchModal.completed')}</span>
<span className='pl-2 font-medium text-[13px] leading-[18px] text-[#155EEF] cursor-pointer' onClick={clearProcessStatus}>{t('datasetDocuments.list.batchModal.ok')}</span>
<span className='pl-2 font-medium text-[13px] leading-[18px] text-text-accent cursor-pointer' onClick={clearProcessStatus}>{t('datasetDocuments.list.batchModal.ok')}</span>
</div>
)}
{importStatus === ProcessStatus.ERROR && (
<div className='inline-flex items-center mr-2 px-3 py-[6px] text-red-600 bg-red-100 rounded-lg border border-black/5'>
<RiErrorWarningFill className='mr-2 w-4 h-4 text-[#D92D20]' />
<span className='font-medium text-[13px] leading-[18px]'>{t('datasetDocuments.list.batchModal.error')}</span>
<span className='pl-2 font-medium text-[13px] leading-[18px] text-[#155EEF] cursor-pointer' onClick={clearProcessStatus}>{t('datasetDocuments.list.batchModal.ok')}</span>
<span className='pl-2 font-medium text-[13px] leading-[18px] text-text-accent cursor-pointer' onClick={clearProcessStatus}>{t('datasetDocuments.list.batchModal.ok')}</span>
</div>
)}
</>
@@ -63,24 +65,43 @@ const SegmentAdd: FC<ISegmentAddProps> = ({
}
return (
<Popover
manualClose
trigger='click'
htmlContent={
<div className='w-full py-1'>
<div className='py-2 px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer text-gray-700 text-sm' onClick={showNewSegmentModal}>{t('datasetDocuments.list.action.add')}</div>
<div className='py-2 px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer text-gray-700 text-sm' onClick={showBatchModal}>{t('datasetDocuments.list.action.batchAdd')}</div>
</div>
}
btnElement={
<div className='inline-flex items-center'>
<FilePlus02 className='w-4 h-4 text-gray-700' />
<span className='pl-1'>{t('datasetDocuments.list.action.addButton')}</span>
</div>
}
btnClassName={open => cn('mr-2 !py-[6px] !text-[13px] !leading-[18px] hover:bg-gray-50 border border-gray-200 hover:border-gray-300 hover:shadow-[0_1px_2px_rgba(16,24,40,0.05)]', open ? '!bg-gray-100 !shadow-none' : '!bg-transparent')}
className='!w-[132px] h-fit !z-20 !translate-x-0 !left-0'
/>
<div className='flex items-center rounded-lg border-[0.5px] border-components-button-secondary-border
bg-components-button-secondary-bg shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px] relative z-20'>
<div
className='inline-flex items-center px-2.5 py-2 border-r-[1px] border-r-divider-subtle cursor-pointer'
onClick={showNewSegmentModal}
>
<RiAddLine className='w-4 h-4 text-components-button-secondary-accent-text' />
<span className='text-components-button-secondary-accent-text text-[13px] leading-[16px] font-medium capitalize px-0.5 ml-0.5'>
{t('datasetDocuments.list.action.addButton')}
</span>
</div>
<Popover
position='br'
manualClose
trigger='click'
htmlContent={
<div className='w-full p-1'>
<div
className='py-1.5 px-2 flex items-center hover:bg-state-base-hover rounded-lg cursor-pointer text-text-secondary system-md-regular'
onClick={showBatchModal}
>
{t('datasetDocuments.list.action.batchAdd')}
</div>
</div>
}
btnElement={
<div className='flex justify-center items-center'>
<RiArrowDownSLine className='w-4 h-4 text-components-button-secondary-accent-text'/>
</div>
}
btnClassName={open => cn('!p-2 !border-0 !rounded-l-none !rounded-r-lg !hover:bg-state-base-hover shadow-xs shadow-shadow-3 backdrop-blur-[5px]',
open ? '!bg-state-base-hover' : '')}
popupClassName='!min-w-[128px] !bg-components-panel-bg-blur !rounded-xl border-[0.5px] !ring-0
border-components-panel-border !shadow-xl !shadow-shadow-shadow-5 backdrop-blur-[5px]'
className='min-w-[128px] h-fit'
/>
</div>
)
}
export default React.memo(SegmentAdd)

View File

@@ -5,11 +5,7 @@
@apply h-6 w-6 !important;
}
.layoutRightIcon {
@apply w-8 h-8 ml-2 box-border border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer hover:shadow-[0_1px_2px_rgba(16,24,40,0.05)];
}
.iconShow {
background: center center url(../assets/layoutRightShow.svg) no-repeat;
}
.iconClose {
background: center center url(../assets/layoutRightClose.svg) no-repeat;
@apply p-2 ml-2 border-[0.5px] border-components-button-secondary-border hover:border-components-button-secondary-border-hover
rounded-lg bg-components-button-secondary-bg hover:bg-components-button-secondary-bg-hover cursor-pointer
shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px];
}

View File

@@ -1,11 +1,15 @@
/* eslint-disable no-mixed-operators */
'use client'
import type { FC, SVGProps } from 'react'
import type { FC } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { useBoolean, useDebounceFn } from 'ahooks'
import { ArrowDownIcon, TrashIcon } from '@heroicons/react/24/outline'
import { ArrowDownIcon } from '@heroicons/react/24/outline'
import { pick } from 'lodash-es'
import {
RiArchive2Line,
RiDeleteBinLine,
RiEditLine,
RiEqualizer2Line,
RiLoopLeftLine,
RiMoreFill,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
@@ -23,7 +27,7 @@ import Popover from '@/app/components/base/popover'
import Confirm from '@/app/components/base/confirm'
import Tooltip from '@/app/components/base/tooltip'
import { ToastContext } from '@/app/components/base/toast'
import type { IndicatorProps } from '@/app/components/header/indicator'
import type { ColorMap, IndicatorProps } from '@/app/components/header/indicator'
import Indicator from '@/app/components/header/indicator'
import { asyncRunSafe } from '@/utils'
import { formatNumber } from '@/utils/format'
@@ -34,30 +38,6 @@ import { DataSourceType, type DocumentDisplayStatus, type SimpleDocumentDetail }
import type { CommonResponse } from '@/models/common'
import useTimestamp from '@/hooks/use-timestamp'
export const SettingsIcon = ({ className }: SVGProps<SVGElement>) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M2 5.33325L10 5.33325M10 5.33325C10 6.43782 10.8954 7.33325 12 7.33325C13.1046 7.33325 14 6.43782 14 5.33325C14 4.22868 13.1046 3.33325 12 3.33325C10.8954 3.33325 10 4.22868 10 5.33325ZM6 10.6666L14 10.6666M6 10.6666C6 11.7712 5.10457 12.6666 4 12.6666C2.89543 12.6666 2 11.7712 2 10.6666C2 9.56202 2.89543 8.66659 4 8.66659C5.10457 8.66659 6 9.56202 6 10.6666Z" stroke="#667085" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
export const SyncIcon = () => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.69773 13.1783C7.29715 13.8879 9.20212 13.8494 10.8334 12.9075C13.5438 11.3427 14.4724 7.87704 12.9076 5.16672L12.7409 4.87804M3.09233 10.8335C1.52752 8.12314 2.45615 4.65746 5.16647 3.09265C6.7978 2.15081 8.70277 2.11227 10.3022 2.82185M1.66226 10.8892L3.48363 11.3773L3.97166 9.5559M12.0284 6.44393L12.5164 4.62256L14.3378 5.1106" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
export const FilePlusIcon = ({ className }: SVGProps<SVGElement>) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M13.3332 6.99992V4.53325C13.3332 3.41315 13.3332 2.85309 13.1152 2.42527C12.9234 2.04895 12.6175 1.74299 12.2412 1.55124C11.8133 1.33325 11.2533 1.33325 10.1332 1.33325H5.8665C4.7464 1.33325 4.18635 1.33325 3.75852 1.55124C3.3822 1.74299 3.07624 2.04895 2.88449 2.42527C2.6665 2.85309 2.6665 3.41315 2.6665 4.53325V11.4666C2.6665 12.5867 2.6665 13.1467 2.88449 13.5746C3.07624 13.9509 3.3822 14.2569 3.75852 14.4486C4.18635 14.6666 4.7464 14.6666 5.8665 14.6666H7.99984M9.33317 7.33325H5.33317M6.6665 9.99992H5.33317M10.6665 4.66659H5.33317M11.9998 13.9999V9.99992M9.99984 11.9999H13.9998" stroke="#667085" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
export const ArchiveIcon = ({ className }: SVGProps<SVGElement>) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M2.66683 5.33106C2.55749 5.32824 2.47809 5.32191 2.40671 5.30771C1.87779 5.2025 1.46432 4.78904 1.35912 4.26012C1.3335 4.13132 1.3335 3.97644 1.3335 3.66667C1.3335 3.3569 1.3335 3.20201 1.35912 3.07321C1.46432 2.54429 1.87779 2.13083 2.40671 2.02562C2.53551 2 2.69039 2 3.00016 2H13.0002C13.3099 2 13.4648 2 13.5936 2.02562C14.1225 2.13083 14.536 2.54429 14.6412 3.07321C14.6668 3.20201 14.6668 3.3569 14.6668 3.66667C14.6668 3.97644 14.6668 4.13132 14.6412 4.26012C14.536 4.78904 14.1225 5.2025 13.5936 5.30771C13.5222 5.32191 13.4428 5.32824 13.3335 5.33106M6.66683 8.66667H9.3335M2.66683 5.33333H13.3335V10.8C13.3335 11.9201 13.3335 12.4802 13.1155 12.908C12.9238 13.2843 12.6178 13.5903 12.2415 13.782C11.8137 14 11.2536 14 10.1335 14H5.86683C4.74672 14 4.18667 14 3.75885 13.782C3.38252 13.5903 3.07656 13.2843 2.88482 12.908C2.66683 12.4802 2.66683 11.9201 2.66683 10.8V5.33333Z" stroke="#667085" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
export const useIndexStatus = () => {
const { t } = useTranslation()
return {
@@ -72,6 +52,15 @@ export const useIndexStatus = () => {
}
}
const STATUS_TEXT_COLOR_MAP: ColorMap = {
green: 'text-util-colors-green-green-600',
orange: 'text-util-colors-warning-warning-600',
red: 'text-util-colors-red-red-600',
blue: 'text-util-colors-blue-light-blue-light-600',
yellow: 'text-util-colors-warning-warning-600',
gray: 'text-text-tertiary',
}
// status item for list
export const StatusItem: FC<{
status: DocumentDisplayStatus
@@ -79,16 +68,75 @@ export const StatusItem: FC<{
scene?: 'list' | 'detail'
textCls?: string
errorMessage?: string
}> = ({ status, reverse = false, scene = 'list', textCls = '', errorMessage }) => {
detail?: {
enabled: boolean
archived: boolean
id: string
}
datasetId?: string
onUpdate?: (operationName?: string) => void
}> = ({ status, reverse = false, scene = 'list', textCls = '', errorMessage, datasetId = '', detail, onUpdate }) => {
const DOC_INDEX_STATUS_MAP = useIndexStatus()
const localStatus = status.toLowerCase() as keyof typeof DOC_INDEX_STATUS_MAP
const { enabled = false, archived = false, id = '' } = detail || {}
const { notify } = useContext(ToastContext)
const { t } = useTranslation()
const onOperate = async (operationName: OperationName) => {
let opApi = deleteDocument
switch (operationName) {
case 'enable':
opApi = enableDocument
break
case 'disable':
opApi = disableDocument
break
}
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, documentId: id }) as Promise<CommonResponse>)
if (!e)
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
else
notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
onUpdate?.(operationName)
}
const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => {
if (operationName === 'enable' && enabled)
return
if (operationName === 'disable' && !enabled)
return
onOperate(operationName)
}, { wait: 500 })
return <div className={
cn('flex items-center',
reverse ? 'flex-row-reverse' : '',
scene === 'detail' ? s.statusItemDetail : '')
}>
<Indicator color={DOC_INDEX_STATUS_MAP[localStatus]?.color as IndicatorProps['color']} className={reverse ? 'ml-2' : 'mr-2'} />
<span className={cn('text-gray-700 text-sm', textCls)}>{DOC_INDEX_STATUS_MAP[localStatus]?.text}</span>
<span className={cn(`${STATUS_TEXT_COLOR_MAP[DOC_INDEX_STATUS_MAP[localStatus].color as keyof typeof STATUS_TEXT_COLOR_MAP]} text-sm`, textCls)}>
{DOC_INDEX_STATUS_MAP[localStatus]?.text}
</span>
{
scene === 'detail' && (
<div className='flex justify-between items-center ml-1.5'>
<Tooltip
popupContent={t('datasetDocuments.list.action.enableWarning')}
popupClassName='text-text-secondary system-xs-medium'
needsDelay
disabled={!archived}
>
<Switch
defaultValue={archived ? false : enabled}
onChange={v => !archived && handleSwitch(v ? 'enable' : 'disable')}
disabled={archived}
size='md'
/>
</Tooltip>
</div>
)
}
{
errorMessage && (
<Tooltip
@@ -216,85 +264,72 @@ export const OperationAction: FC<{
</>
)}
{embeddingAvailable && (
<Popover
htmlContent={
<div className='w-full py-1'>
{!isListScene && <>
<div className='flex justify-between items-center mx-4 pt-2'>
<span className={cn(s.actionName, 'font-medium')}>
{!archived && enabled ? t('datasetDocuments.list.index.enable') : t('datasetDocuments.list.index.disable')}
</span>
<Tooltip
popupContent={t('datasetDocuments.list.action.enableWarning')}
popupClassName='!font-semibold'
needsDelay
disabled={!archived}
>
<div>
<Switch
defaultValue={archived ? false : enabled}
onChange={v => !archived && handleSwitch(v ? 'enable' : 'disable')}
disabled={archived}
size='md'
/>
<>
<Tooltip
popupContent={t('datasetDocuments.list.action.settings')}
popupClassName='text-text-secondary system-xs-medium'
needsDelay
>
<div
className={cn('rounded-lg mr-2 cursor-pointer',
!isListScene
? 'p-2 bg-components-button-secondary-bg hover:bg-components-button-secondary-bg-hover border-[0.5px] border-components-button-secondary-border hover:border-components-button-secondary-border-hover shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px]'
: 'p-0.5 hover:bg-state-base-hover')}
onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}>
<RiEqualizer2Line className='w-4 h-4 text-components-button-secondary-text' />
</div>
</Tooltip>
<Popover
htmlContent={
<div className='w-full py-1'>
{!archived && (
<>
<div className={s.actionItem} onClick={() => {
handleShowRenameModal({
id: detail.id,
name: detail.name,
})
}}>
<RiEditLine className='w-4 h-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.table.rename')}</span>
</div>
</Tooltip>
</div>
<div className='mx-4 pb-1 pt-0.5 text-xs text-gray-500'>
{!archived && enabled ? t('datasetDocuments.list.index.enableTip') : t('datasetDocuments.list.index.disableTip')}
</div>
<Divider />
</>}
{!archived && (
<>
<div className={s.actionItem} onClick={() => {
handleShowRenameModal({
id: detail.id,
name: detail.name,
})
}}>
<Edit03 className='w-4 h-4 text-gray-500' />
<span className={s.actionName}>{t('datasetDocuments.list.table.rename')}</span>
{['notion_import', DataSourceType.WEB].includes(data_source_type) && (
<div className={s.actionItem} onClick={() => onOperate('sync')}>
<RiLoopLeftLine className='w-4 h-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.sync')}</span>
</div>
)}
<Divider className='my-1' />
</>
)}
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
<RiArchive2Line className='w-4 h-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
</div>}
{archived && (
<div className={s.actionItem} onClick={() => onOperate('un_archive')}>
<RiArchive2Line className='w-4 h-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.unarchive')}</span>
</div>
<div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}>
<SettingsIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.settings')}</span>
</div>
{['notion_import', DataSourceType.WEB].includes(data_source_type) && (
<div className={s.actionItem} onClick={() => onOperate('sync')}>
<SyncIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.sync')}</span>
</div>
)}
<Divider className='my-1' />
</>
)}
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
<ArchiveIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
</div>}
{archived && (
<div className={s.actionItem} onClick={() => onOperate('un_archive')}>
<ArchiveIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.unarchive')}</span>
)}
<div className={cn(s.actionItem, s.deleteActionItem, 'group')} onClick={() => setShowModal(true)}>
<RiDeleteBinLine className={'w-4 h-4 text-text-tertiary group-hover:text-text-destructive'} />
<span className={cn(s.actionName, 'group-hover:text-text-destructive')}>{t('datasetDocuments.list.action.delete')}</span>
</div>
)}
<div className={cn(s.actionItem, s.deleteActionItem, 'group')} onClick={() => setShowModal(true)}>
<TrashIcon className={'w-4 h-4 stroke-current text-gray-500 stroke-2 group-hover:text-red-500'} />
<span className={cn(s.actionName, 'group-hover:text-red-500')}>{t('datasetDocuments.list.action.delete')}</span>
</div>
</div>
}
trigger='click'
position='br'
btnElement={
<div className={cn(s.commonIcon)}>
<RiMoreFill className='w-4 h-4 text-gray-700' />
</div>
}
btnClassName={open => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!bg-gray-100 !shadow-none' : '!bg-transparent')}
className={`flex justify-end !w-[200px] h-fit !z-20 ${className}`}
/>
}
trigger='click'
position='br'
btnElement={
<div className={cn(s.commonIcon)}>
<RiMoreFill className='w-4 h-4 text-text-components-button-secondary-text' />
</div>
}
btnClassName={open => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!bg-gray-100 !shadow-none' : '!bg-transparent')}
popupClassName='!w-full'
className={`flex justify-end !w-[200px] h-fit !z-20 ${className}`}
/>
</>
)}
{showModal
&& <Confirm

View File

@@ -18,16 +18,18 @@
@apply h-6 w-6 rounded-md border-none p-1 hover:bg-gray-100 !important;
}
.actionIconWrapperDetail {
@apply h-8 w-8 p-2 hover:bg-gray-50 border border-gray-200 hover:border-gray-300 hover:shadow-[0_1px_2px_rgba(16,24,40,0.05)] !important;
@apply p-2 bg-components-button-secondary-bg hover:bg-components-button-secondary-bg-hover
border-[0.5px] border-components-button-secondary-border hover:border-components-button-secondary-border-hover
shadow-xs shadow-shadow-shadow-3 !important;
}
.actionItem {
@apply h-9 py-2 px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer;
}
.deleteActionItem {
@apply hover:bg-red-50 !important;
@apply hover:bg-state-destructive-hover !important;
}
.actionName {
@apply text-gray-700 text-sm;
@apply text-text-secondary text-sm;
}
.addFileBtn {
@apply mt-4 w-fit !text-[13px] text-primary-600 font-medium bg-white border-[0.5px];
@@ -94,7 +96,8 @@
background-image: url(~@/assets/docx.svg);
}
.statusItemDetail {
@apply h-8 font-medium border border-gray-200 inline-flex items-center rounded-lg pl-3 pr-4 mr-2;
@apply border-[0.5px] border-components-button-secondary-border inline-flex items-center
rounded-lg pl-2.5 pr-2 py-2 mr-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px];
}
.tdValue {
@apply text-sm overflow-hidden text-ellipsis whitespace-nowrap;

View File

@@ -77,7 +77,7 @@ const ModifyRetrievalModal: FC<Props> = ({
<div className='text-base font-semibold text-gray-900'>
<div>{t('datasetSettings.form.retrievalSetting.title')}</div>
<div className='leading-[18px] text-xs font-normal text-gray-500'>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
{t('datasetSettings.form.retrievalSetting.description')}
</div>
</div>

View File

@@ -305,7 +305,7 @@ const Form = () => {
<div>
<div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.retrievalSetting.title')}</div>
<div className='leading-[18px] text-xs font-normal text-gray-500'>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
{t('datasetSettings.form.retrievalSetting.description')}
</div>
</div>

View File

@@ -7,7 +7,7 @@ export type IndicatorProps = {
className?: string
}
type ColorMap = {
export type ColorMap = {
green: string
orange: string
red: string
@@ -17,28 +17,28 @@ type ColorMap = {
}
const BACKGROUND_MAP: ColorMap = {
green: 'bg-[#31C48D]',
orange: 'bg-[#FF5A1F]',
red: 'bg-[#F04438]',
blue: 'bg-[#36BFFA]',
yellow: 'bg-[#FDB022]',
gray: 'bg-[#D0D5DD]',
green: 'bg-components-badge-status-light-success-bg',
orange: 'bg-components-badge-status-light-warning-bg',
red: 'bg-components-badge-status-light-error-bg',
blue: 'bg-components-badge-status-light-normal-bg',
yellow: 'bg-components-badge-status-light-warning-bg',
gray: 'bg-components-badge-status-light-disabled-bg',
}
const BORDER_MAP: ColorMap = {
green: 'border-[#0E9F6E]',
orange: 'border-[#D03801]',
red: 'border-[#D92D20]',
blue: 'border-[#0BA5EC]',
yellow: 'border-[#F79009]',
gray: 'border-[#98A2B3]',
green: 'border-components-badge-status-light-success-border-inner',
orange: 'border-components-badge-status-light-warning-border-inner',
red: 'border-components-badge-status-light-error-border-inner',
blue: 'border-components-badge-status-light-normal-border-inner',
yellow: 'border-components-badge-status-light-warning-border-inner',
gray: 'border-components-badge-status-light-disabled-border-inner',
}
const SHADOW_MAP: ColorMap = {
green: 'shadow-[0_0_5px_-3px_rgba(14,159,110,0.1),0.5px_0.5px_3px_rgba(14,159,110,0.3),inset_1.5px_1.5px_0px_rgba(255,255,255,0.2)]',
orange: 'shadow-[0_0_5px_-3px_rgba(255,90,31,0.2),0.5px_0.5px_3px_rgba(255, 90, 31, 0.3), inset_1.5px_1.5px_0_rgba(255, 255, 255, 0.2)]',
red: 'shadow-[0_0_5px_-3px_rgba(249,112,102,0.1),0.5px_0.5px_3px_rgba(249, 112, 102, 0.2), inset_1.5px_1.5px_0_rgba(255, 255, 255, 0.4)]',
blue: 'shadow-[0_0_5px_-3px_rgba(208, 213, 221, 0.1),0.5px_0.5px_3px_rgba(208, 213, 221, 0.3), inset_1.5px_1.5px_0_rgba(255, 255, 255, 0.2)]',
yellow: 'shadow-[0_0_5px_-3px_rgba(253, 176, 34, 0.1),0.5px_0.5px_3px_rgba(253, 176, 34, 0.3), inset_1.5px_1.5px_0_rgba(255, 255, 255, 0.2)]',
gray: 'shadow-[0_0_5px_-3px_rgba(208, 213, 221, 0.1),0.5px_0.5px_3px_rgba(208, 213, 221, 0.3), inset_1.5px_1.5px_0_rgba(255, 255, 255, 0.2)]',
green: 'shadow-status-indicator-green-shadow',
orange: 'shadow-status-indicator-warning-shadow',
red: 'shadow-status-indicator-red-shadow',
blue: 'shadow-status-indicator-blue-shadow',
yellow: 'shadow-status-indicator-warning-shadow',
gray: 'shadow-status-indicator-gray-shadow',
}
export default function Indicator({

View File

@@ -0,0 +1,12 @@
'use client'
import { useState } from 'react'
import { InputNumber } from '../components/base/input-number'
// import { Stepper } from '../components/datasets/create/stepper'
export default function Page() {
const [step, setStep] = useState(0)
return <div className='p-4'>
<InputNumber onChange={setStep} unit={'tokens'} />
</div>
}

View File

@@ -99,6 +99,16 @@ const translation = {
autoDescription: 'Automatically set chunk and preprocessing rules. Unfamiliar users are recommended to select this.',
custom: 'Custom',
customDescription: 'Customize chunks rules, chunks length, and preprocessing rules, etc.',
general: 'General',
generalTip: 'General text chunking mode, the chunks retrieved and recalled are the same.',
parentChild: 'Parent-child',
parentChildTip: 'When using the parent-child mode, the child-chunk is used for retrieval and the parent-chunk is used for recall as context.',
parentChunkForContext: 'Parent-chunk for Context',
childChunkForRetrieval: 'Child-chunk for Retrieval',
paragraph: 'Paragraph',
paragraphTip: 'This mode splits the text in to paragraphs based on delimiters and the maximum chunk length, using the split text as the parent chunk for retrieval.',
fullDoc: 'Full Doc',
fullDocTip: 'The entire document is used as the parent chunk and retrieved directly. Please note that for performance reasons, text exceeding 10000 tokens will be automatically truncated.',
separator: 'Delimiter',
separatorTip: 'A delimiter is the character used to separate text. \\n\\n and \\n are commonly used delimiters for separating paragraphs and lines. Combined with commas (\\n\\n,\\n), paragraphs will be segmented by lines when exceeding the maximum chunk length. You can also use special delimiters defined by yourself (e.g. ***).',
separatorPlaceholder: '\\n\\n for separating paragraphs; \\n for separating lines',
@@ -112,6 +122,7 @@ const translation = {
removeUrlEmails: 'Delete all URLs and email addresses',
removeStopwords: 'Remove stopwords such as "a", "an", "the"',
preview: 'Confirm & Preview',
previewChunk: 'Preview Chunk',
reset: 'Reset',
indexMode: 'Index mode',
qualified: 'High Quality',

View File

@@ -99,6 +99,16 @@ const translation = {
autoDescription: '自动设置分段规则与预处理规则,如果不了解这些参数建议选择此项',
custom: '自定义',
customDescription: '自定义分段规则、分段长度以及预处理规则等参数',
general: '通用',
generalTip: '通用文本分块模式,检索和回忆的块是相同的',
parentChild: '父子分段',
parentChildTip: '使用父子模式时,子块用于检索,父块用作上下文',
parentChunkForContext: '父块用作上下文',
childChunkForRetrieval: '子块用于检索',
paragraph: '段落',
paragraphTip: '此模式根据分隔符和最大块长度将文本拆分为段落,使用拆分文本作为检索的母块',
fullDoc: '全文',
fullDocTip: '整个文档用作父块并直接检索。请注意出于性能原因超过10000个标记的文本将被自动截断。',
separator: '分段标识符',
separatorTip: '分隔符是用于分隔文本的字符。\\n\\n 和 \\n 是常用于分隔段落和行的分隔符。用逗号连接分隔符(\\n\\n,\\n当段落超过最大块长度时会按行进行分割。你也可以使用自定义的特殊分隔符例如 ***)。',
separatorPlaceholder: '\\n\\n 用于分段;\\n 用于分行',
@@ -112,6 +122,7 @@ const translation = {
removeUrlEmails: '删除所有 URL 和电子邮件地址',
removeStopwords: '去除停用词,例如 “a”“an”“the” 等',
preview: '确认并预览',
previewChunk: '预览块',
reset: '重置',
indexMode: '索引方式',
qualified: '高质量',

View File

@@ -170,7 +170,9 @@ export type IndexingStatusBatchResponse = {
data: IndexingStatusResponse[]
}
export type ProcessMode = 'automatic' | 'custom'
export type ProcessMode = 'custom' | 'hierarchical'
export type ParentMode = 'full-doc' | 'paragraph'
export type ProcessRuleResponse = {
mode: ProcessMode
@@ -180,6 +182,8 @@ export type ProcessRuleResponse = {
export type Rules = {
pre_processing_rules: PreProcessingRule[]
segmentation: Segmentation
parent_node: ParentMode
subchunk_segmentation: Segmentation
}
export type PreProcessingRule = {
@@ -190,7 +194,7 @@ export type PreProcessingRule = {
export type Segmentation = {
separator: string
max_tokens: number
chunk_overlap: number
chunk_overlap?: number
}
export const DocumentIndexingStatusList = [
@@ -336,6 +340,11 @@ export type createDocumentResponse = {
documents: InitialDocumentDetail[]
}
export type PrecessRule = {
mode: ProcessMode
rules: Rules
}
export type FullDocumentDetail = SimpleDocumentDetail & {
batch: string
created_api_request_id: string
@@ -358,6 +367,7 @@ export type FullDocumentDetail = SimpleDocumentDetail & {
doc_type?: DocType | null | 'others'
doc_metadata?: DocMetadata | null
segment_count: number
dataset_process_rule: PrecessRule
[key: string]: any
}

View File

@@ -80,6 +80,11 @@ module.exports = {
'xl': '0px 8px 8px -4px rgba(16, 24, 40, 0.03), 0px 20px 24px -4px rgba(16, 24, 40, 0.08)',
'2xl': '0px 24px 48px -12px rgba(16, 24, 40, 0.18)',
'3xl': '0px 32px 64px -12px rgba(16, 24, 40, 0.14)',
'status-indicator-green-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-success-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
'status-indicator-warning-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-warning-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
'status-indicator-red-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-error-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
'status-indicator-blue-shadow': '0px 2px 6px 0px var(--color-components-badge-status-light-normal-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
'status-indicator-gray-shadow': '0px 1px 2px 0px var(--color-components-badge-status-light-disabled-halo), 0px 0px 0px 1px var(--color-components-badge-status-light-border-outer)',
},
opacity: {
2: '0.02',