This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
function escape(input: string): string {
if (!input || typeof input !== 'string')
return ''
const res = input
// .replaceAll('\\', '\\\\') // This would add too many backslashes
.replaceAll('\0', '\\0')
.replaceAll('\b', '\\b')
.replaceAll('\f', '\\f')
.replaceAll('\n', '\\n')
.replaceAll('\r', '\\r')
.replaceAll('\t', '\\t')
.replaceAll('\v', '\\v')
.replaceAll('\'', '\\\'')
return res
}
export default escape

View File

@@ -0,0 +1,413 @@
.pageHeader {
@apply px-16 flex justify-between items-center;
position: sticky;
top: 0;
left: 0;
padding-top: 42px;
padding-bottom: 12px;
background-color: #fff;
font-weight: 600;
font-size: 18px;
line-height: 28px;
color: #101828;
z-index: 10;
}
.segmentationItem {
min-height: 68px;
}
.indexItem {
min-height: 126px;
}
.indexItem .disableMask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 12px;
z-index: 2;
}
.indexItem .warningTip {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 8px 20px 8px 40px;
background: #FFFAEB;
border-top: 0.5px solid #FEF0C7;
border-radius: 12px;
font-size: 12px;
line-height: 18px;
color: #344054;
z-index: 3;
}
.indexItem .warningTip::before {
content: '';
position: absolute;
top: 11px;
left: 20px;
width: 12px;
height: 12px;
background: center no-repeat url(../assets/alert-triangle.svg);
background-size: 12px;
}
.indexItem .warningTip .click {
color: #155EEF;
cursor: pointer;
}
.disabled {
cursor: not-allowed !important;
}
.indexItem.disabled:hover {
background-color: #fcfcfd;
border-color: #f2f4f7;
box-shadow: none;
cursor: default;
}
.indexItem.disabled:hover .radio {
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
}
.radioItem {
@apply relative mb-2 rounded-xl border border-components-option-card-option-border cursor-pointer bg-components-option-card-option-bg;
}
.radioItem.segmentationItem.custom {
height: auto;
}
.radioItem.segmentationItem.custom .typeHeader {
/* height: 65px; */
}
.radioItem.indexItem .typeHeader {
@apply py-4 pr-5;
}
.radioItem.indexItem.active .typeHeader {
padding: 15.5px 19.5px 15.5px 63.5px;
}
.radioItem.indexItem .radio {
top: 16px;
right: 20px;
}
.radioItem.indexItem.active .radio {
top: 16px;
right: 19.5px;
}
.radioItem.indexItem .typeHeader .title {
@apply pb-1;
}
.radioItem .typeIcon {
position: absolute;
top: 18px;
left: 20px;
width: 32px;
height: 32px;
background: #EEF4FF center no-repeat;
border-radius: 8px;
}
.typeIcon.auto {
background-color: #F5F3FF;
background-image: url(../assets/zap-fast.svg);
}
.typeIcon.customize {
background-image: url(../assets/sliders-02.svg);
}
.typeIcon.qualified {
background-color: #FFF6ED;
background-image: url(../assets/star-07.svg);
}
.typeIcon.economical {
background-image: url(../assets/piggy-bank-mod.svg);
}
.radioItem .radio {
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
position: absolute;
top: 26px;
right: 20px;
}
.radioItem:hover {
background-color: #ffffff;
border-color: #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.radioItem:hover .radio {
border-color: #155eef;
}
.radioItem.active {
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.radioItem.active .radio {
top: 25.5px;
right: 19.5px;
border-width: 5px;
border-color: #155EEF;
}
.radioItem.active:hover {
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.radioItem.active .typeIcon {
top: 17.5px;
left: 19.5px;
}
.radioItem.active .typeHeader {
padding: 11.5px 63.5px;
}
.typeHeader {
@apply flex flex-col px-16 py-3 justify-center;
}
.typeHeader .title {
display: flex;
align-items: center;
padding-bottom: 2px;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #101828;
}
.typeHeader .tip {
font-weight: 400;
font-size: 13px;
line-height: 18px;
color: #667085;
}
.recommendTag {
display: inline-flex;
justify-content: center;
align-items: center;
padding: 0 6px;
margin-left: 4px;
border: 1px solid #E0EAFF;
border-radius: 6px;
font-weight: 500;
font-size: 12px;
line-height: 20px;
color: #444CE7;
}
.typeFormBody {
@apply px-16;
border-top: 1px solid #F2F4F7;
}
.formRow {
@apply flex justify-between mt-6;
}
.formRow .label {
@apply mb-2 p-0;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #101828;
}
.ruleItem {
@apply flex items-center py-1.5;
}
.formFooter {
padding: 16px 0 28px;
}
.formFooter .button {
font-size: 13px;
line-height: 18px;
}
.input {
@apply inline-flex h-9 w-full py-1 px-2 pr-14 rounded-lg text-xs leading-normal;
@apply bg-gray-100 caret-primary-600 hover:bg-gray-100 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none focus:bg-white placeholder:text-gray-400;
}
.source {
@apply flex justify-between items-center mt-8 px-6 py-4 rounded-xl bg-gray-50 border border-gray-100;
}
.source .divider {
@apply shrink-0 mx-4 w-px bg-gray-200;
height: 42px;
}
.fileIcon {
@apply inline-flex mr-1 w-6 h-6 bg-center bg-no-repeat;
background-image: url(../assets/pdf.svg);
background-size: 24px;
}
.fileIcon.pdf {
background-image: url(../assets/pdf.svg);
}
.fileIcon.csv {
background-image: url(../assets/csv.svg);
}
.fileIcon.doc {
background-image: url(../assets/doc.svg);
}
.fileIcon.docx {
background-image: url(../assets/docx.svg);
}
.fileIcon.xlsx,
.fileIcon.xls {
background-image: url(../assets/xlsx.svg);
}
.fileIcon.html,
.fileIcon.htm {
background-image: url(../assets/html.svg);
}
.fileIcon.md,
.fileIcon.markdown {
background-image: url(../assets/md.svg);
}
.fileIcon.txt {
background-image: url(../assets/txt.svg);
}
.fileIcon.json {
background-image: url(../assets/json.svg);
}
.sourceContent {
width: 0;
flex: 1 1 auto;
}
.sourceCount {
@apply shrink-0 ml-1;
font-weight: 500;
font-size: 13px;
line-height: 18px;
color: #667085;
}
.segmentCount {
flex: 1 1 30%;
max-width: 120px;
}
.divider {
@apply mx-3 w-px h-4 bg-gray-200;
}
.calculating {
color: #98A2B3;
font-size: 12px;
line-height: 18px;
}
.sideTip {
@apply flex flex-col items-center shrink-0;
padding-top: 108px;
width: 524px;
border-left: 0.5px solid #F2F4F7;
}
.tipCard {
@apply flex flex-col items-start p-6;
width: 320px;
background-color: #F9FAFB;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
border-radius: 12px;
}
.tipCard .icon {
width: 32px;
height: 32px;
border: 1px solid #EAECF0;
border-radius: 6px;
background: center no-repeat url(../assets/book-open-01.svg);
background-size: 16px;
}
.tipCard .title {
margin: 12px 0;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.tipCard .content {
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #344054;
}
.previewWrap {
flex-shrink: 0;
width: 524px;
}
.previewWrap.isMobile {
max-width: 524px;
}
/*
* `fixed` must under `previewHeader` because of style override would not work
*/
.fixed {
padding-top: 12px;
font-size: 12px;
line-height: 18px;
background: rgba(255, 255, 255, 0.9);
border-bottom: 0.5px solid #EAECF0;
backdrop-filter: blur(4px);
animation: fix 0.5s;
}
@keyframes fix {
from {
padding-top: 42px;
font-size: 18px;
line-height: 28px;
}
to {
padding-top: 12px;
font-size: 12px;
line-height: 18px;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
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-xs font-semibold leading-none text-text-secondary'>{props.children}</label>
}
const FormField: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
return <div className='flex-1 space-y-2'>
<TextLabel>{props.label}</TextLabel>
{props.children}
</div>
}
export const DelimiterInput: FC<InputProps & { tooltip?: string }> = (props) => {
const { t } = useTranslation()
return <FormField label={<div className='mb-1 flex items-center'>
<span className='system-sm-semibold mr-0.5'>{t('datasetCreation.stepTwo.separator')}</span>
<Tooltip
popupContent={
<div className='max-w-[200px]'>
{props.tooltip || 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 maxValue = Number.parseInt(globalThis.document?.body?.getAttribute('data-public-indexing-max-segmentation-tokens-length') || '4000', 10)
const { t } = useTranslation()
return <FormField label={<div className='system-sm-semibold mb-1'>
{t('datasetCreation.stepTwo.maxLength')}
</div>}>
<InputNumber
type="number"
size='large'
placeholder={`${maxValue}`}
max={maxValue}
min={1}
{...props}
/>
</FormField>
}
export const OverlapInput: FC<InputNumberProps> = (props) => {
const { t } = useTranslation()
return <FormField label={<div className='mb-1 flex items-center'>
<span className='system-sm-semibold'>{t('datasetCreation.stepTwo.overlap')}</span>
<Tooltip
popupContent={
<div className='max-w-[200px]'>
{t('datasetCreation.stepTwo.overlapTip')}
</div>
}
/>
</div>}>
<InputNumber
type="number"
size='large'
placeholder={t('datasetCreation.stepTwo.overlap') || ''}
min={1}
{...props}
/>
</FormField>
}

View File

@@ -0,0 +1,62 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import Popover from '@/app/components/base/popover'
import { languages } from '@/i18n-config/language'
export type ILanguageSelectProps = {
currentLanguage: string
onSelect: (language: string) => void
disabled?: boolean
}
const LanguageSelect: FC<ILanguageSelectProps> = ({
currentLanguage,
onSelect,
disabled,
}) => {
return (
<Popover
manualClose
trigger='click'
disabled={disabled}
popupClassName='z-20'
htmlContent={
<div className='w-full p-1'>
{languages.filter(language => language.supported).map(({ prompt_name }) => (
<div
key={prompt_name}
className='inline-flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 hover:bg-state-base-hover'
onClick={() => onSelect(prompt_name)}
>
<span className='system-sm-medium text-text-secondary'>{prompt_name}</span>
{(currentLanguage === prompt_name) && <RiCheckLine className='size-4 text-text-accent' />}
</div>
))}
</div>
}
btnElement={
<div className={cn('inline-flex items-center gap-x-[1px]', disabled && 'cursor-not-allowed')}>
<span className={cn(
'system-xs-semibold px-[3px] text-components-button-tertiary-text',
disabled ? 'text-components-button-tertiary-text-disabled' : '',
)}>
{currentLanguage}
</span>
<RiArrowDownSLine className={cn(
'size-3.5 text-components-button-tertiary-text',
disabled ? 'text-components-button-tertiary-text-disabled' : '',
)} />
</div>
}
btnClassName={() => cn(
'!hover:bg-components-button-tertiary-bg !mx-1 rounded-md !border-0 !bg-components-button-tertiary-bg !px-1.5 !py-1',
disabled ? 'bg-components-button-tertiary-bg-disabled' : '',
)}
className='!left-1 !z-20 h-fit !w-[140px] !translate-x-0'
/>
)
}
export default React.memo(LanguageSelect)

View File

@@ -0,0 +1,108 @@
import type { ComponentProps, FC, ReactNode } from 'react'
import Image from 'next/image'
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="currentColor" />
</svg>
)
type OptionCardHeaderProps = {
icon: ReactNode
title: ReactNode
description: string
isActive?: boolean
activeClassName?: string
effectImg?: string
disabled?: boolean
}
export const OptionCardHeader: FC<OptionCardHeaderProps> = (props) => {
const { icon, title, description, isActive, activeClassName, effectImg, disabled } = props
return <div className={classNames(
'relative flex h-full overflow-hidden rounded-t-xl',
isActive && activeClassName,
!disabled && 'cursor-pointer',
)}>
<div className='relative flex size-14 items-center justify-center overflow-hidden'>
{isActive && effectImg && <Image src={effectImg} className='absolute left-0 top-0 h-full w-full' alt='' width={56} height={56} />}
<div className='p-1'>
<div className='flex size-8 justify-center rounded-lg border border-components-panel-border-subtle bg-background-default-dodge p-1.5 shadow-md'>
{icon}
</div>
</div>
</div>
<TriangleArrow
className={classNames('absolute -bottom-1.5 left-4 text-transparent', isActive && 'text-components-panel-bg')}
/>
<div className='flex-1 space-y-0.5 py-3 pr-4'>
<div className='system-md-semibold text-text-secondary'>{title}</div>
<div className='system-xs-regular text-text-tertiary'>{description}</div>
</div>
</div>
}
type OptionCardProps = {
icon: ReactNode
className?: string
activeHeaderClassName?: string
title: ReactNode
description: string
isActive?: boolean
actions?: ReactNode
effectImg?: string
onSwitched?: () => void
noHighlight?: boolean
disabled?: boolean
} & Omit<ComponentProps<'div'>, 'title' | 'onClick'>
export const OptionCard: FC<OptionCardProps> = (
{
ref,
...props
},
) => {
const { icon, className, title, description, isActive, children, actions, activeHeaderClassName, style, effectImg, onSwitched, noHighlight, disabled, ...rest } = props
return (
<div
className={classNames(
'rounded-xl bg-components-option-card-option-bg shadow-xs',
(isActive && !noHighlight)
? 'border-[1.5px] border-components-option-card-option-selected-border'
: 'border border-components-option-card-option-border',
disabled && 'pointer-events-none opacity-50',
className,
)}
style={{
...style,
}}
onClick={() => {
if (!isActive && !disabled)
onSwitched?.()
}}
{...rest}
ref={ref}
>
<OptionCardHeader
icon={icon}
title={title}
description={description}
isActive={isActive && !noHighlight}
activeClassName={activeHeaderClassName}
effectImg={effectImg}
disabled={disabled}
/>
{/** Body */}
{isActive && (children || actions) && <div className='rounded-b-xl bg-components-panel-bg px-4 py-3'>
{children}
{actions && <div className='mt-4 flex gap-2'>
{actions}
</div>
}
</div>}
</div>
)
}
OptionCard.displayName = 'OptionCard'

View File

@@ -0,0 +1,78 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
export type IPreviewItemProps = {
type: string
index: number
content?: string
qa?: {
answer: string
question: string
}
}
export enum PreviewType {
TEXT = 'text',
QA = 'QA',
}
const sharpIcon = (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.74999 1.5L3.24999 10.5M8.74998 1.5L7.24998 10.5M10.25 4H1.75M9.75 8H1.25" stroke="#98A2B3" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
const textIcon = (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 3.5H8M6 3.5V8.5M3.9 10.5H8.1C8.94008 10.5 9.36012 10.5 9.68099 10.3365C9.96323 10.1927 10.1927 9.96323 10.3365 9.68099C10.5 9.36012 10.5 8.94008 10.5 8.1V3.9C10.5 3.05992 10.5 2.63988 10.3365 2.31901C10.1927 2.03677 9.96323 1.8073 9.68099 1.66349C9.36012 1.5 8.94008 1.5 8.1 1.5H3.9C3.05992 1.5 2.63988 1.5 2.31901 1.66349C2.03677 1.8073 1.8073 2.03677 1.66349 2.31901C1.5 2.63988 1.5 3.05992 1.5 3.9V8.1C1.5 8.94008 1.5 9.36012 1.66349 9.68099C1.8073 9.96323 2.03677 10.1927 2.31901 10.3365C2.63988 10.5 3.05992 10.5 3.9 10.5Z" stroke="#667085" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
const PreviewItem: FC<IPreviewItemProps> = ({
type = PreviewType.TEXT,
index,
content,
qa,
}) => {
const { t } = useTranslation()
const charNums = type === PreviewType.TEXT
? (content || '').length
: (qa?.answer || '').length + (qa?.question || '').length
const formattedIndex = (() => String(index).padStart(3, '0'))()
return (
<div className='rounded-xl bg-gray-50 p-4'>
<div className='flex h-5 items-center justify-between text-xs text-gray-500'>
<div className='box-border flex h-[18px] items-center space-x-1 rounded-md border border-gray-200 pl-1 pr-1.5 font-medium italic'>
{sharpIcon}
<span>{formattedIndex}</span>
</div>
<div className='flex items-center space-x-1'>
{textIcon}
<span>{charNums} {t('datasetCreation.stepTwo.characters')}</span>
</div>
</div>
<div className='mt-2 line-clamp-6 max-h-[120px] overflow-hidden text-sm text-gray-800'>
{type === PreviewType.TEXT && (
<div style={{ whiteSpace: 'pre-line' }}>{content}</div>
)}
{type === PreviewType.QA && (
<div style={{ whiteSpace: 'pre-line' }}>
<div className='flex'>
<div className='text-medium mr-2 shrink-0 text-gray-400'>Q</div>
<div style={{ whiteSpace: 'pre-line' }}>{qa?.question}</div>
</div>
<div className='flex'>
<div className='text-medium mr-2 shrink-0 text-gray-400'>A</div>
<div style={{ whiteSpace: 'pre-line' }}>{qa?.answer}</div>
</div>
</div>
)}
</div>
</div>
)
}
export default React.memo(PreviewItem)

View File

@@ -0,0 +1,54 @@
// https://github.com/iamakulov/unescape-js/blob/master/src/index.js
/**
* \\ - matches the backslash which indicates the beginning of an escape sequence
* (
* u\{([0-9A-Fa-f]+)\} - first alternative; matches the variable-length hexadecimal escape sequence (\u{ABCD0})
* |
* u([0-9A-Fa-f]{4}) - second alternative; matches the 4-digit hexadecimal escape sequence (\uABCD)
* |
* x([0-9A-Fa-f]{2}) - third alternative; matches the 2-digit hexadecimal escape sequence (\xA5)
* |
* ([1-7][0-7]{0,2}|[0-7]{2,3}) - fourth alternative; matches the up-to-3-digit octal escape sequence (\5 or \512)
* |
* (['"tbrnfv0\\]) - fifth alternative; matches the special escape characters (\t, \n and so on)
* |
* \U([0-9A-Fa-f]+) - sixth alternative; matches the 8-digit hexadecimal escape sequence used by python (\U0001F3B5)
* )
*/
const jsEscapeRegex = /\\(u\{([0-9A-Fa-f]+)\}|u([0-9A-Fa-f]{4})|x([0-9A-Fa-f]{2})|([1-7][0-7]{0,2}|[0-7]{2,3})|(['"tbrnfv0\\]))|\\U([0-9A-Fa-f]{8})/g
const usualEscapeSequences: Record<string, string> = {
'0': '\0',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
'v': '\v',
'\'': '\'',
'"': '"',
'\\': '\\',
}
const fromHex = (str: string) => String.fromCodePoint(Number.parseInt(str, 16))
const fromOct = (str: string) => String.fromCodePoint(Number.parseInt(str, 8))
const unescape = (str: string) => {
return str.replace(jsEscapeRegex, (_, __, varHex, longHex, shortHex, octal, specialCharacter, python) => {
if (varHex !== undefined)
return fromHex(varHex)
else if (longHex !== undefined)
return fromHex(longHex)
else if (shortHex !== undefined)
return fromHex(shortHex)
else if (octal !== undefined)
return fromOct(octal)
else if (python !== undefined)
return fromHex(python)
else
return usualEscapeSequences[specialCharacter]
})
}
export default unescape