From 655e596106c0a7fb8025cd9a784019caad84ec76 Mon Sep 17 00:00:00 2001
From: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Date: Mon, 10 Aug 2026 22:51:39 +0800
Subject: [PATCH] refactor(web): share new RAG setup controls
---
web/features/new-rag/add-source-page.tsx | 75 +++------------
.../retrieval-mode-segmented-control.tsx | 56 +++++++++++
.../new-rag/connected-source-setup.tsx | 95 ++-----------------
.../new-rag/knowledge-settings-form.tsx | 38 +++-----
web/features/new-rag/retrieval-test-page.tsx | 40 +++-----
web/features/new-rag/source-setup-fields.tsx | 94 ++++++++++++++++++
6 files changed, 199 insertions(+), 199 deletions(-)
create mode 100644 web/features/new-rag/components/retrieval-mode-segmented-control.tsx
diff --git a/web/features/new-rag/add-source-page.tsx b/web/features/new-rag/add-source-page.tsx
index 1daadb5c645..88f34c443ce 100644
--- a/web/features/new-rag/add-source-page.tsx
+++ b/web/features/new-rag/add-source-page.tsx
@@ -44,8 +44,9 @@ import {
sourceProviderOptionForDraft,
} from './source-provider-options'
import {
+ SourceProviderIcon,
SourceProviderNotInstalledCard,
- SourceProviderRadioGroup,
+ SourceProviderSelector,
SourceTypeSelector,
} from './source-setup-fields'
import { WebsiteCrawlPreview } from './website-crawl-preview'
@@ -207,64 +208,6 @@ function getSupportedAuthKinds(provider: Provider, credentialId?: string) {
return supported
}
-function ProviderSelector({
- disabled = false,
- onMoreProviders,
- options,
- providerKey,
- onChange,
-}: {
- disabled?: boolean
- onMoreProviders: () => void
- options: SourceProviderOption[]
- providerKey: string
- onChange: (providerKey: string) => void
-}) {
- const { t } = useTranslation('dataset')
-
- return (
-
- )
-}
-
-function WebsiteProviderIcon({ option }: { option: SourceProviderOption }) {
- const icon = option.installed
- ? (option.datasource.identity.icon ?? option.plugin.declaration.identity.icon)
- : undefined
- if (typeof icon === 'string' && icon)
- return
- return
-}
-
function ProviderFieldControl({
field,
setValues,
@@ -680,7 +623,15 @@ function UnconfiguredProvider({
return (
-
+
{t(($) => $['newKnowledge.providerNotConfigured'], {
@@ -1311,7 +1262,7 @@ export function AddSourcePage({
/>
{sourceDraft.sourceType === 'websiteCrawl' ? (
<>
-
) : websiteProviderOption && !websiteProviderOption.installed ? (
}
+ icon={}
provider={websiteProviderOption.label}
onInstall={() =>
globalThis.open(
diff --git a/web/features/new-rag/components/retrieval-mode-segmented-control.tsx b/web/features/new-rag/components/retrieval-mode-segmented-control.tsx
new file mode 100644
index 00000000000..c83a5f38c72
--- /dev/null
+++ b/web/features/new-rag/components/retrieval-mode-segmented-control.tsx
@@ -0,0 +1,56 @@
+'use client'
+
+import { cn } from '@langgenius/dify-ui/cn'
+import { SegmentedControl, SegmentedControlItem } from '@langgenius/dify-ui/segmented-control'
+import { useTranslation } from 'react-i18next'
+
+const retrievalModes = ['fast', 'deep', 'research'] as const
+
+export type RetrievalMode = (typeof retrievalModes)[number]
+
+export function RetrievalModeSegmentedControl({
+ 'aria-label': ariaLabel,
+ 'aria-labelledby': ariaLabelledBy,
+ appearance = 'default',
+ disabled = false,
+ value,
+ onChange,
+}: {
+ 'aria-label'?: string
+ 'aria-labelledby'?: string
+ appearance?: 'composer' | 'default'
+ disabled?: boolean
+ value: RetrievalMode
+ onChange: (value: RetrievalMode) => void
+}) {
+ const { t } = useTranslation('dataset')
+
+ return (
+
+ aria-label={ariaLabel}
+ aria-labelledby={ariaLabelledBy}
+ className={cn(
+ appearance === 'composer' && 'flex min-w-46.5 gap-0.5 bg-background-section-burn',
+ )}
+ value={[value]}
+ onValueChange={(values) => {
+ const nextValue = values[0]
+ if (nextValue) onChange(nextValue)
+ }}
+ >
+ {retrievalModes.map((mode) => (
+
+ key={mode}
+ value={mode}
+ disabled={disabled}
+ className={cn(
+ appearance === 'composer' &&
+ 'grow border-0 px-2.5 py-1.25 system-sm-regular text-text-tertiary capitalize data-pressed:bg-components-panel-bg data-pressed:font-medium data-pressed:text-text-primary',
+ )}
+ >
+ {t(($) => $[`newKnowledge.settings.retrievalMode.${mode}`])}
+
+ ))}
+
+ )
+}
diff --git a/web/features/new-rag/connected-source-setup.tsx b/web/features/new-rag/connected-source-setup.tsx
index 25eacd3cbb2..b8fd07821ca 100644
--- a/web/features/new-rag/connected-source-setup.tsx
+++ b/web/features/new-rag/connected-source-setup.tsx
@@ -15,11 +15,9 @@ import type {
DataSourceAuth,
DataSourceCredential,
} from '@/app/components/header/account-setting/data-source-page-new/types'
-import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
import { Button } from '@langgenius/dify-ui/button'
import { Checkbox } from '@langgenius/dify-ui/checkbox'
import { cn } from '@langgenius/dify-ui/cn'
-import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
import { useInfiniteQuery, useQuery, useQueryClient } from '@tanstack/react-query'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -46,8 +44,9 @@ import {
import {
SourceConnectionRequiredCard,
SourceNameField,
+ SourceProviderIcon,
SourceProviderNotInstalledCard,
- SourceProviderRadioGroup,
+ SourceProviderSelector,
SourceSyncPolicyField,
} from './source-setup-fields'
@@ -142,11 +141,9 @@ function datasourceProviderForOption(option?: SourceProviderOption) {
: undefined
}
-type ProviderBrandIconValue = DataSourceItem['declaration']['identity']['icon']
-
function datasourceProviderIcon(
datasourceProvider: ReturnType,
-): ProviderBrandIconValue | undefined {
+) {
return datasourceProvider?.plugin.declaration.identity.icon
}
@@ -315,83 +312,6 @@ function requestStatus(error: unknown) {
return undefined
}
-function ProviderSelector({
- options,
- providerKey,
- onChange,
- onMoreProviders,
-}: {
- options: SourceProviderOption[]
- providerKey: string
- onChange: (providerKey: string) => void
- onMoreProviders: () => void
-}) {
- const { t } = useTranslation('dataset')
- return (
-
- )
-}
-
-function ProviderBrandIcon({
- fallbackIcon,
- icon,
-}: {
- fallbackIcon: string
- icon?: ProviderBrandIconValue
-}) {
- if (typeof icon === 'string' && icon)
- return
-
- if (icon && typeof icon !== 'string')
- return (
-
- {icon.content}
-
- )
-
- return
-}
-
function OAuthConnectionCard({
draft,
icon,
@@ -399,7 +319,7 @@ function OAuthConnectionCard({
onConnect,
}: {
draft: ConnectedSourceDraft
- icon?: ProviderBrandIconValue
+ icon?: ReturnType
providerOption: InstalledSourceProviderOption
onConnect: () => void
}) {
@@ -414,7 +334,7 @@ function OAuthConnectionCard({
provider: draft.provider,
})
}
- icon={}
+ icon={}
title={
draft.provider === 'Notion'
? t(($) => $['newKnowledge.notionNotConnected'])
@@ -1824,7 +1744,8 @@ export function ConnectedSourceSetup({
}
return (
-
) : providerOption && !providerOption.installed ? (
}
+ icon={}
provider={providerOption.label}
onInstall={() =>
globalThis.open(
diff --git a/web/features/new-rag/knowledge-settings-form.tsx b/web/features/new-rag/knowledge-settings-form.tsx
index eb0841739a5..fe086a1f43e 100644
--- a/web/features/new-rag/knowledge-settings-form.tsx
+++ b/web/features/new-rag/knowledge-settings-form.tsx
@@ -24,7 +24,6 @@ import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Form } from '@langgenius/dify-ui/form'
import { Input } from '@langgenius/dify-ui/input'
-import { SegmentedControl, SegmentedControlItem } from '@langgenius/dify-ui/segmented-control'
import { Slider } from '@langgenius/dify-ui/slider'
import { Switch } from '@langgenius/dify-ui/switch'
import { Textarea } from '@langgenius/dify-ui/textarea'
@@ -40,6 +39,7 @@ import { useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
import { KnowledgeSettingsMembers } from './components/knowledge-settings-members'
import { KnowledgeSpaceIcon } from './components/knowledge-space-icon'
+import { RetrievalModeSegmentedControl } from './components/retrieval-mode-segmented-control'
import { isKnowledgeModelSetupReady, KNOWLEDGE_NAME_MAX_LENGTH } from './constants'
import { newKnowledgeListPath } from './routes'
@@ -1024,30 +1024,22 @@ export function KnowledgeSettingsForm({
>
{t(($) => $['newKnowledge.settings.retrievalDepth'])}
- {
- const mode = values[0]
- if (mode === 'fast' || mode === 'deep' || mode === 'research') {
- const nextScoreThresholdEnabled =
- mode !== 'research' && !rerankEnabled ? false : scoreThresholdEnabled
- setRetrievalMode(mode)
- setScoreThresholdEnabled(nextScoreThresholdEnabled)
- void performSettingsSave({
- ...currentSettingsDraft,
- retrievalMode: mode,
- scoreThresholdEnabled: nextScoreThresholdEnabled,
- })
- }
+ disabled={retrievalFieldsDisabled}
+ value={retrievalMode}
+ onChange={(mode) => {
+ const nextScoreThresholdEnabled =
+ mode !== 'research' && !rerankEnabled ? false : scoreThresholdEnabled
+ setRetrievalMode(mode)
+ setScoreThresholdEnabled(nextScoreThresholdEnabled)
+ void performSettingsSave({
+ ...currentSettingsDraft,
+ retrievalMode: mode,
+ scoreThresholdEnabled: nextScoreThresholdEnabled,
+ })
}}
- >
- {(['fast', 'deep', 'research'] as const).map((mode) => (
-
- {t(($) => $[`newKnowledge.settings.retrievalMode.${mode}`])}
-
- ))}
-
+ />
diff --git a/web/features/new-rag/retrieval-test-page.tsx b/web/features/new-rag/retrieval-test-page.tsx
index 159dc79e2de..168a8730653 100644
--- a/web/features/new-rag/retrieval-test-page.tsx
+++ b/web/features/new-rag/retrieval-test-page.tsx
@@ -26,6 +26,7 @@ import { Markdown } from '@/app/components/base/markdown'
import { Link as MarkdownLink } from '@/app/components/base/markdown-blocks'
import Link from '@/next/link'
import { consoleClient, consoleQuery } from '@/service/client'
+import { RetrievalModeSegmentedControl } from './components/retrieval-mode-segmented-control'
import {
extractRetrievalEvidence,
extractStreamError,
@@ -1589,34 +1590,19 @@ export function RetrievalTestPage({ knowledgeSpaceId }: { knowledgeSpaceId: stri
}}
/>
-
$['newKnowledge.settings.retrievalModeLabel'])}
- className="flex min-w-46.5 gap-0.5 rounded-lg bg-background-section-burn p-0.5"
- >
- {(['fast', 'deep', 'research'] as const).map((item) => (
-
- ))}
-
+ appearance="composer"
+ disabled={selectedResearchActive || localRun?.status === 'running'}
+ value={mode}
+ onChange={(nextMode) =>
+ setComposerDraft({
+ mode: nextMode,
+ query,
+ ...(selectedHistoryKey ? { selectionKey: selectedHistoryKey } : {}),
+ })
+ }
+ />