mirror of
https://github.com/langgenius/dify.git
synced 2026-07-23 20:18:40 +08:00
235 lines
8.3 KiB
TypeScript
235 lines
8.3 KiB
TypeScript
import type { GetSystemFeaturesResponse } from '@dify/contracts/api/console/system-features/types.gen'
|
|
import type { FC, RefObject } from 'react'
|
|
import type { InputValueTypes, TextGenerationCustomConfig, TextGenerationRunControl } from './types'
|
|
import type { PromptConfig, SavedMessage, TextToSpeechConfig } from '@/models/debug'
|
|
import type { SiteInfo } from '@/models/share'
|
|
import type { VisionFile, VisionSettings } from '@/types/app'
|
|
import { cn } from '@langgenius/dify-ui/cn'
|
|
import { Tabs, TabsList, TabsPanel, TabsTab } from '@langgenius/dify-ui/tabs'
|
|
import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react'
|
|
import { useCallback, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import SavedItems from '@/app/components/app/text-generate/saved-items'
|
|
import AppIcon from '@/app/components/base/app-icon'
|
|
import Badge from '@/app/components/base/badge'
|
|
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
|
import { appDefaultIconBackground } from '@/config'
|
|
import { AccessMode } from '@/models/access-control'
|
|
import MenuDropdown from './menu-dropdown'
|
|
import RunBatch from './run-batch'
|
|
import RunOnce from './run-once'
|
|
|
|
type TextGenerationSidebarProps = {
|
|
accessMode: AccessMode
|
|
allTasksRun: boolean
|
|
currentTab: string
|
|
customConfig: TextGenerationCustomConfig | null
|
|
inputs: Record<string, InputValueTypes>
|
|
inputsRef: RefObject<Record<string, InputValueTypes>>
|
|
isInstalledApp: boolean
|
|
isPC: boolean
|
|
isWorkflow: boolean
|
|
onBatchSend: (data: string[][]) => void
|
|
onInputsChange: (inputs: Record<string, InputValueTypes>) => void
|
|
onRemoveSavedMessage: (messageId: string) => Promise<void>
|
|
onRunOnceSend: () => void
|
|
onTabChange: (tab: string) => void
|
|
onVisionFilesChange: (files: VisionFile[]) => void
|
|
promptConfig: PromptConfig
|
|
resultExisted: boolean
|
|
runControl: TextGenerationRunControl | null
|
|
savedMessages: SavedMessage[]
|
|
siteInfo: SiteInfo
|
|
systemFeatures: GetSystemFeaturesResponse
|
|
textToSpeechConfig: TextToSpeechConfig | null
|
|
visionConfig: VisionSettings
|
|
}
|
|
|
|
const TextGenerationSidebar: FC<TextGenerationSidebarProps> = ({
|
|
accessMode,
|
|
allTasksRun,
|
|
currentTab,
|
|
customConfig,
|
|
inputs,
|
|
inputsRef,
|
|
isInstalledApp,
|
|
isPC,
|
|
isWorkflow,
|
|
onBatchSend,
|
|
onInputsChange,
|
|
onRemoveSavedMessage,
|
|
onRunOnceSend,
|
|
onTabChange,
|
|
onVisionFilesChange,
|
|
promptConfig,
|
|
resultExisted,
|
|
runControl,
|
|
savedMessages,
|
|
siteInfo,
|
|
systemFeatures,
|
|
textToSpeechConfig,
|
|
visionConfig,
|
|
}) => {
|
|
const { t } = useTranslation()
|
|
const [descExpanded, setDescExpanded] = useState(false)
|
|
const [showDescToggle, setShowDescToggle] = useState(false)
|
|
const handleDescRef = useCallback((node: HTMLDivElement | null) => {
|
|
setShowDescToggle(!!node && node.scrollHeight > node.clientHeight)
|
|
}, [])
|
|
|
|
return (
|
|
<Tabs
|
|
value={currentTab}
|
|
onValueChange={onTabChange}
|
|
className={cn(
|
|
'relative flex h-full shrink-0 flex-col',
|
|
isPC ? 'w-[600px] max-w-[50%]' : resultExisted ? 'h-[calc(100%-64px)]' : '',
|
|
isInstalledApp && 'rounded-l-2xl',
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
'shrink-0 space-y-4 border-b border-divider-subtle',
|
|
isPC ? 'bg-components-panel-bg p-8 pb-0' : 'p-4 pb-0',
|
|
)}
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<AppIcon
|
|
size={isPC ? 'large' : 'small'}
|
|
iconType={siteInfo.icon_type}
|
|
icon={siteInfo.icon}
|
|
background={siteInfo.icon_background || appDefaultIconBackground}
|
|
imageUrl={siteInfo.icon_url}
|
|
/>
|
|
<div className="grow truncate system-md-semibold text-text-secondary">
|
|
{siteInfo.title}
|
|
</div>
|
|
<MenuDropdown
|
|
hideLogout={isInstalledApp || accessMode === AccessMode.PUBLIC}
|
|
data={siteInfo}
|
|
/>
|
|
</div>
|
|
{siteInfo.description && (
|
|
<div>
|
|
<div
|
|
ref={handleDescRef}
|
|
className={cn(
|
|
'relative system-xs-regular break-words whitespace-pre-wrap text-text-tertiary',
|
|
!descExpanded && 'line-clamp-3',
|
|
descExpanded && 'max-h-32 overflow-y-auto',
|
|
)}
|
|
>
|
|
{siteInfo.description}
|
|
{!descExpanded && showDescToggle && (
|
|
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-6 bg-linear-to-b from-components-panel-bg-transparent to-components-panel-bg" />
|
|
)}
|
|
</div>
|
|
{showDescToggle && (
|
|
<button
|
|
type="button"
|
|
className="mt-0.5 flex items-center gap-0.5 system-xs-regular text-text-accent hover:opacity-80"
|
|
onClick={() => setDescExpanded((v) => !v)}
|
|
>
|
|
{descExpanded ? (
|
|
<>
|
|
<RiArrowUpSLine className="size-3" />
|
|
{t(($) => $['chat.collapse'], { ns: 'share' })}
|
|
</>
|
|
) : (
|
|
<>
|
|
<RiArrowDownSLine className="size-3" />
|
|
{t(($) => $['chat.expand'], { ns: 'share' })}
|
|
</>
|
|
)}
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
<TabsList className="w-full">
|
|
<TabsTab value="create">
|
|
<span className="ml-2">{t(($) => $['generation.tabs.create'], { ns: 'share' })}</span>
|
|
</TabsTab>
|
|
<TabsTab value="batch">
|
|
<span className="ml-2">{t(($) => $['generation.tabs.batch'], { ns: 'share' })}</span>
|
|
</TabsTab>
|
|
{!isWorkflow && (
|
|
<TabsTab value="saved" className="ml-auto">
|
|
<span aria-hidden className="i-ri-bookmark-3-line size-4" />
|
|
<span className="ml-2">{t(($) => $['generation.tabs.saved'], { ns: 'share' })}</span>
|
|
{savedMessages.length > 0 && <Badge className="ml-1">{savedMessages.length}</Badge>}
|
|
</TabsTab>
|
|
)}
|
|
</TabsList>
|
|
</div>
|
|
<div
|
|
className={cn(
|
|
'h-0 grow overflow-y-auto bg-components-panel-bg',
|
|
isPC ? 'px-8' : 'px-4',
|
|
!isPC &&
|
|
resultExisted &&
|
|
customConfig?.remove_webapp_brand &&
|
|
'rounded-b-2xl border-b-[0.5px] border-divider-regular',
|
|
)}
|
|
>
|
|
<TabsPanel value="create" keepMounted>
|
|
<RunOnce
|
|
siteInfo={siteInfo}
|
|
inputs={inputs}
|
|
inputsRef={inputsRef}
|
|
onInputsChange={onInputsChange}
|
|
promptConfig={promptConfig}
|
|
onSend={onRunOnceSend}
|
|
visionConfig={visionConfig}
|
|
onVisionFilesChange={onVisionFilesChange}
|
|
runControl={runControl}
|
|
/>
|
|
</TabsPanel>
|
|
<TabsPanel value="batch" keepMounted>
|
|
<RunBatch
|
|
vars={promptConfig.prompt_variables}
|
|
onSend={onBatchSend}
|
|
isAllFinished={allTasksRun}
|
|
/>
|
|
</TabsPanel>
|
|
{!isWorkflow && (
|
|
<TabsPanel value="saved">
|
|
<SavedItems
|
|
className={cn(isPC ? 'mt-6' : 'mt-4')}
|
|
isShowTextToSpeech={textToSpeechConfig?.enabled}
|
|
list={savedMessages}
|
|
onRemove={onRemoveSavedMessage}
|
|
onStartCreateContent={() => onTabChange('create')}
|
|
/>
|
|
</TabsPanel>
|
|
)}
|
|
</div>
|
|
{!customConfig?.remove_webapp_brand && (
|
|
<div
|
|
className={cn(
|
|
'flex shrink-0 items-center gap-1.5 bg-components-panel-bg py-3',
|
|
isPC ? 'px-8' : 'px-4',
|
|
!isPC && resultExisted && 'rounded-b-2xl border-b-[0.5px] border-divider-regular',
|
|
)}
|
|
>
|
|
<div className="system-2xs-medium-uppercase text-text-tertiary">
|
|
{t(($) => $['chat.poweredBy'], { ns: 'share' })}
|
|
</div>
|
|
{systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo ? (
|
|
<img
|
|
src={systemFeatures.branding.workspace_logo}
|
|
alt="logo"
|
|
className="block h-5 w-auto"
|
|
/>
|
|
) : customConfig?.replace_webapp_logo ? (
|
|
<img src={customConfig.replace_webapp_logo} alt="logo" className="block h-5 w-auto" />
|
|
) : (
|
|
<DifyLogo size="small" />
|
|
)}
|
|
</div>
|
|
)}
|
|
</Tabs>
|
|
)
|
|
}
|
|
|
|
export default TextGenerationSidebar
|