mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-09 23:10:20 +08:00
refactor(AgentSettings): restructure settings components for better reusability
- Replace SettingsInline with more flexible SettingsItem component - Add SettingsContainer for consistent layout - Remove redundant styled components in favor of shared components
This commit is contained in:
parent
ea62294bd8
commit
f49d3791b6
@ -5,8 +5,7 @@ import { Input } from 'antd'
|
|||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { SettingDivider } from '..'
|
import { AgentLabel, SettingsContainer, SettingsItem, SettingsTitle } from './shared'
|
||||||
import { AgentLabel, SettingsInline, SettingsTitle } from './shared'
|
|
||||||
|
|
||||||
interface AgentEssentialSettingsProps {
|
interface AgentEssentialSettingsProps {
|
||||||
agent: AgentEntity | undefined | null
|
agent: AgentEntity | undefined | null
|
||||||
@ -26,27 +25,28 @@ const AgentEssentialSettings: FC<AgentEssentialSettingsProps> = ({ agent, update
|
|||||||
if (!agent) return null
|
if (!agent) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-1 flex-col overflow-hidden">
|
<SettingsContainer>
|
||||||
<SettingsInline>
|
<SettingsItem inline>
|
||||||
<SettingsTitle>{t('agent.type.label')}</SettingsTitle>
|
<SettingsTitle>{t('agent.type.label')}</SettingsTitle>
|
||||||
<AgentLabel type={agent.type} />
|
<AgentLabel type={agent.type} />
|
||||||
</SettingsInline>
|
</SettingsItem>
|
||||||
<SettingDivider />
|
<SettingsItem>
|
||||||
<SettingsTitle>{t('common.name')}</SettingsTitle>
|
<SettingsTitle>{t('common.name')}</SettingsTitle>
|
||||||
<HStack gap={8} alignItems="center">
|
<HStack gap={8} alignItems="center">
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('common.assistant') + t('common.name')}
|
placeholder={t('common.assistant') + t('common.name')}
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
if (name !== agent.name) {
|
if (name !== agent.name) {
|
||||||
onUpdate()
|
onUpdate()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
</div>
|
</SettingsItem>
|
||||||
|
</SettingsContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SettingsTitle } from './shared'
|
import { SettingsContainer, SettingsItem, SettingsTitle } from './shared'
|
||||||
|
|
||||||
interface AgentPromptSettingsProps {
|
interface AgentPromptSettingsProps {
|
||||||
agent: AgentEntity | undefined | null
|
agent: AgentEntity | undefined | null
|
||||||
@ -51,70 +51,65 @@ const AgentPromptSettings: FC<AgentPromptSettingsProps> = ({ agent, update }) =>
|
|||||||
if (!agent) return null
|
if (!agent) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<SettingsContainer>
|
||||||
<SettingsTitle>
|
<SettingsItem divider={false} className="flex-1">
|
||||||
{t('common.prompt')}
|
<SettingsTitle>
|
||||||
<Popover title={t('agents.add.prompt.variables.tip.title')} content={promptVarsContent}>
|
{t('common.prompt')}
|
||||||
<HelpCircle size={14} color="var(--color-text-2)" />
|
<Popover title={t('agents.add.prompt.variables.tip.title')} content={promptVarsContent}>
|
||||||
</Popover>
|
<HelpCircle size={14} color="var(--color-text-2)" />
|
||||||
</SettingsTitle>
|
</Popover>
|
||||||
<TextAreaContainer>
|
</SettingsTitle>
|
||||||
<RichEditorContainer>
|
<TextAreaContainer>
|
||||||
{showPreview ? (
|
<RichEditorContainer>
|
||||||
<MarkdownContainer
|
{showPreview ? (
|
||||||
onDoubleClick={() => {
|
<MarkdownContainer
|
||||||
const currentScrollTop = editorRef.current?.getScrollTop?.() || 0
|
onDoubleClick={() => {
|
||||||
|
const currentScrollTop = editorRef.current?.getScrollTop?.() || 0
|
||||||
|
setShowPreview(false)
|
||||||
|
requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop))
|
||||||
|
}}>
|
||||||
|
<ReactMarkdown>{processedPrompt || instructions}</ReactMarkdown>
|
||||||
|
</MarkdownContainer>
|
||||||
|
) : (
|
||||||
|
<CodeEditor
|
||||||
|
value={instructions}
|
||||||
|
language="markdown"
|
||||||
|
onChange={setInstructions}
|
||||||
|
height="100%"
|
||||||
|
expanded={false}
|
||||||
|
style={{
|
||||||
|
height: '100%'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</RichEditorContainer>
|
||||||
|
</TextAreaContainer>
|
||||||
|
<HSpaceBetweenStack width="100%" justifyContent="flex-end" mt="10px">
|
||||||
|
<TokenCount>Tokens: {tokenCount}</TokenCount>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={showPreview ? <Edit size={14} /> : <Save size={14} />}
|
||||||
|
onClick={() => {
|
||||||
|
const currentScrollTop = editorRef.current?.getScrollTop?.() || 0
|
||||||
|
if (showPreview) {
|
||||||
setShowPreview(false)
|
setShowPreview(false)
|
||||||
requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop))
|
requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop))
|
||||||
}}>
|
} else {
|
||||||
<ReactMarkdown>{processedPrompt || instructions}</ReactMarkdown>
|
onUpdate()
|
||||||
</MarkdownContainer>
|
requestAnimationFrame(() => {
|
||||||
) : (
|
setShowPreview(true)
|
||||||
<CodeEditor
|
requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop))
|
||||||
value={instructions}
|
})
|
||||||
language="markdown"
|
}
|
||||||
onChange={setInstructions}
|
}}>
|
||||||
height="100%"
|
{showPreview ? t('common.edit') : t('common.save')}
|
||||||
expanded={false}
|
</Button>
|
||||||
style={{
|
</HSpaceBetweenStack>
|
||||||
height: '100%'
|
</SettingsItem>
|
||||||
}}
|
</SettingsContainer>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</RichEditorContainer>
|
|
||||||
</TextAreaContainer>
|
|
||||||
<HSpaceBetweenStack width="100%" justifyContent="flex-end" mt="10px">
|
|
||||||
<TokenCount>Tokens: {tokenCount}</TokenCount>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={showPreview ? <Edit size={14} /> : <Save size={14} />}
|
|
||||||
onClick={() => {
|
|
||||||
const currentScrollTop = editorRef.current?.getScrollTop?.() || 0
|
|
||||||
if (showPreview) {
|
|
||||||
setShowPreview(false)
|
|
||||||
requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop))
|
|
||||||
} else {
|
|
||||||
onUpdate()
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
setShowPreview(true)
|
|
||||||
requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
{showPreview ? t('common.edit') : t('common.save')}
|
|
||||||
</Button>
|
|
||||||
</HSpaceBetweenStack>
|
|
||||||
</Container>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
`
|
|
||||||
|
|
||||||
const TextAreaContainer = styled.div`
|
const TextAreaContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -4,14 +4,12 @@ import { getAgentTypeLabel } from '@renderer/i18n/label'
|
|||||||
import { AgentType } from '@renderer/types'
|
import { AgentType } from '@renderer/types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { SettingDivider } from '..'
|
||||||
|
|
||||||
export const SettingsTitle: React.FC<React.PropsWithChildren> = ({ children }) => {
|
export const SettingsTitle: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||||
return <div className="mb-1 flex items-center gap-2 font-bold">{children}</div>
|
return <div className="mb-1 flex items-center gap-2 font-bold">{children}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsInline: React.FC<React.PropsWithChildren> = ({ children }) => {
|
|
||||||
return <div className="flex items-center justify-between gap-2">{children}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AgentLabelProps = {
|
export type AgentLabelProps = {
|
||||||
type: AgentType
|
type: AgentType
|
||||||
name?: string
|
name?: string
|
||||||
@ -31,3 +29,37 @@ export const AgentLabel: React.FC<AgentLabelProps> = ({ type, name, classNames,
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SettingsItemProps extends React.ComponentPropsWithRef<'div'> {
|
||||||
|
/** Add a divider beneath the item if true, defaults to true. */
|
||||||
|
divider?: boolean
|
||||||
|
/** Apply row direction flex or not, defaults to false. */
|
||||||
|
inline?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingsItem: React.FC<SettingsItemProps> = ({
|
||||||
|
children,
|
||||||
|
divider = true,
|
||||||
|
inline = false,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
className={cn('flex flex-col', inline ? 'flex-row items-center justify-between gap-2' : undefined, className)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
{divider && <SettingDivider />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingsContainer: React.FC<React.ComponentPropsWithRef<'div'>> = ({ children, className, ...props }) => {
|
||||||
|
return (
|
||||||
|
<div className={cn('flex flex-1 flex-col overflow-hidden', className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user