refactor(ui): improve settings tab and assistant item UI (#11819)

* refactor(ui): improve settings tab and assistant item UI

- Remove SettingsTab from HomeTabs, open settings via navbar drawer instead
- Add menu icon to assistant/agent items for quick access to settings popup
- Remove SessionSettingsTab component (consolidated into settings popup)
- Restore avatar display in bubble message style
- Update topic/session item styles for consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(ui): simplify MessageHeader component logic

- Removed unnecessary header visibility check in MessageHeader.
- Updated justifyContent logic for UserWrap to account for multi-select mode.

This change enhances the clarity and maintainability of the MessageHeader component.

* refactor(ui): streamline ChatNavbar and SettingsTab components

- Removed unused chat state from ChatNavbar.
- Updated SettingsTab to conditionally render settings based on active topic or session.
- Enhanced clarity and maintainability by reducing unnecessary checks and improving component logic.

This change improves the overall user experience and code readability.

* refactor(ui): enhance AgentItem and ChatNavbar components for improved UI

- Updated AgentItem to conditionally hide the assistant icon based on settings.
- Enhanced ChatNavbar to display the assistant's emoji and name with a new layout.
- Introduced memoization for assistant name to optimize rendering.

These changes improve the user interface and maintainability of the components.

* refactor(ui): update HtmlArtifactsPopup to start in fullscreen mode

- Changed initial state of isFullscreen in HtmlArtifactsPopup from false to true.

This adjustment enhances the user experience by providing a more immersive view upon opening the popup.

* refactor(types): remove 'settings' tab from Tab type

- Updated the Tab type in chat.ts to remove the 'settings' option, simplifying the available tabs for the chat interface.

This change streamlines the chat functionality and improves code clarity.

* refactor(ui): enhance UserWrap styling in MessageHeader component

- Added flex property to UserWrap to improve layout flexibility.

This change enhances the responsiveness and layout management of the MessageHeader component.

* refactor(ui): update HtmlArtifactsPopup to prevent drag on ViewControls

- Added "nodrag" class to ViewControls to prevent drag events on double click.

This change improves the user interaction by ensuring that double-clicking on the ViewControls does not trigger drag actions.

* refactor(ui): adjust spacing in AgentLabel component

- Updated the gap between items in the AgentLabel component from 1 to 2 for improved layout consistency.

This change enhances the visual spacing and overall user interface of the AgentSettings page.

* refactor(ui): remove unused useSessions hook from AgentItem component

- Eliminated the useSessions hook from the AgentItem component to streamline the code and improve performance.

This change enhances the maintainability of the AgentItem component by removing unnecessary dependencies.

* refactor(ui): optimize MessageHeader component layout and logic

- Introduced a memoized userNameJustifyContent calculation to streamline the justifyContent logic for UserWrap.
- Simplified the HStack component by replacing inline logic with the new memoized value.

These changes enhance the maintainability and clarity of the MessageHeader component.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
亢奋猫 2025-12-11 16:11:21 +08:00 committed by GitHub
parent 5f3af646f4
commit 367c4fe6b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 201 additions and 413 deletions

View File

@ -25,7 +25,7 @@ type ViewMode = 'split' | 'code' | 'preview'
const HtmlArtifactsPopup: React.FC<HtmlArtifactsPopupProps> = ({ open, title, html, onSave, onClose }) => { const HtmlArtifactsPopup: React.FC<HtmlArtifactsPopupProps> = ({ open, title, html, onSave, onClose }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [viewMode, setViewMode] = useState<ViewMode>('split') const [viewMode, setViewMode] = useState<ViewMode>('split')
const [isFullscreen, setIsFullscreen] = useState(false) const [isFullscreen, setIsFullscreen] = useState(true)
const [saved, setSaved] = useTemporaryValue(false, 2000) const [saved, setSaved] = useTemporaryValue(false, 2000)
const codeEditorRef = useRef<CodeEditorHandles>(null) const codeEditorRef = useRef<CodeEditorHandles>(null)
const previewFrameRef = useRef<HTMLIFrameElement>(null) const previewFrameRef = useRef<HTMLIFrameElement>(null)
@ -78,7 +78,7 @@ const HtmlArtifactsPopup: React.FC<HtmlArtifactsPopupProps> = ({ open, title, ht
</HeaderLeft> </HeaderLeft>
<HeaderCenter> <HeaderCenter>
<ViewControls onDoubleClick={(e) => e.stopPropagation()}> <ViewControls onDoubleClick={(e) => e.stopPropagation()} className="nodrag">
<ViewButton <ViewButton
size="small" size="small"
type={viewMode === 'split' ? 'primary' : 'default'} type={viewMode === 'split' ? 'primary' : 'default'}

View File

@ -89,10 +89,7 @@ const Sidebar: FC = () => {
)} )}
</MainMenusContainer> </MainMenusContainer>
<Menus> <Menus>
<Tooltip <Tooltip title={t('settings.theme.title') + ': ' + getThemeModeLabel(settedTheme)} placement="right">
title={t('settings.theme.title') + ': ' + getThemeModeLabel(settedTheme)}
mouseEnterDelay={0.8}
placement="right">
<Icon theme={theme} onClick={toggleTheme}> <Icon theme={theme} onClick={toggleTheme}>
{settedTheme === ThemeMode.dark ? ( {settedTheme === ThemeMode.dark ? (
<Moon size={20} className="icon" /> <Moon size={20} className="icon" />

View File

@ -19,6 +19,7 @@ import styled from 'styled-components'
import AssistantsDrawer from './components/AssistantsDrawer' import AssistantsDrawer from './components/AssistantsDrawer'
import ChatNavbarContent from './components/ChatNavbarContent' import ChatNavbarContent from './components/ChatNavbarContent'
import SettingsButton from './components/SettingsButton'
import UpdateAppButton from './components/UpdateAppButton' import UpdateAppButton from './components/UpdateAppButton'
interface Props { interface Props {
@ -65,14 +66,6 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
}) })
} }
// const handleUpdateModel = useCallback(
// async (model: ApiModel) => {
// if (!activeSession || !activeAgent) return
// return updateModel(activeSession.id, model.id, { showSuccessToast: false })
// },
// [activeAgent, activeSession, updateModel]
// )
return ( return (
<NavbarHeader className="home-navbar" style={{ height: 'var(--navbar-height)' }}> <NavbarHeader className="home-navbar" style={{ height: 'var(--navbar-height)' }}>
<div className="flex h-full min-w-0 flex-1 shrink items-center overflow-auto"> <div className="flex h-full min-w-0 flex-1 shrink items-center overflow-auto">
@ -107,6 +100,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
</div> </div>
<HStack alignItems="center" gap={8}> <HStack alignItems="center" gap={8}>
{isTopNavbar && <UpdateAppButton />} {isTopNavbar && <UpdateAppButton />}
<SettingsButton assistant={assistant} />
{isTopNavbar && ( {isTopNavbar && (
<Tooltip title={t('navbar.expand')} mouseEnterDelay={0.8}> <Tooltip title={t('navbar.expand')} mouseEnterDelay={0.8}>
<NarrowIcon onClick={handleNarrowModeToggle}> <NarrowIcon onClick={handleNarrowModeToggle}>

View File

@ -429,7 +429,7 @@ const FileBlocksContainer = styled.div`
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
padding: 0 15px; padding: 0;
margin: 8px 0; margin: 8px 0;
background: transparent; background: transparent;
border-radius: 4px; border-radius: 4px;

View File

@ -83,11 +83,11 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message, topic, isGro
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [model?.provider, showMinappIcon]) }, [model?.provider, showMinappIcon])
const hideHeader = isBubbleStyle ? isUserMessage && !isMultiSelectMode : false const userNameJustifyContent = useMemo(() => {
if (!isBubbleStyle) return 'flex-start'
if (hideHeader) { if (isUserMessage && !isMultiSelectMode) return 'flex-end'
return null return 'flex-start'
} }, [isBubbleStyle, isUserMessage, isMultiSelectMode])
return ( return (
<Container className="message-header"> <Container className="message-header">
@ -121,7 +121,7 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message, topic, isGro
</> </>
)} )}
<UserWrap> <UserWrap>
<HStack alignItems="center"> <HStack alignItems="center" justifyContent={userNameJustifyContent}>
<UserName isBubbleStyle={isBubbleStyle} theme={theme}> <UserName isBubbleStyle={isBubbleStyle} theme={theme}>
{username} {username}
</UserName> </UserName>

View File

@ -63,7 +63,10 @@ const Prompt: FC<Props> = ({ assistant, topic }) => {
} }
return ( return (
<Container className="system-prompt" onClick={() => AssistantSettingsPopup.show({ assistant })} $isDark={isDark}> <Container
className="system-prompt"
onClick={() => AssistantSettingsPopup.show({ assistant, tab: 'prompt' })}
$isDark={isDark}>
<Text $isVisible={isVisible}>{displayText}</Text> <Text $isVisible={isVisible}>{displayText}</Text>
</Container> </Container>
) )

View File

@ -1,45 +0,0 @@
import type { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
import { SettingDivider } from '@renderer/pages/settings'
import { SessionSettingsPopup } from '@renderer/pages/settings/AgentSettings'
import AdvancedSettings from '@renderer/pages/settings/AgentSettings/AdvancedSettings'
import EssentialSettings from '@renderer/pages/settings/AgentSettings/EssentialSettings'
import type { GetAgentSessionResponse } from '@renderer/types'
import { Button } from 'antd'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
interface Props {
session: GetAgentSessionResponse | undefined | null
update: ReturnType<typeof useUpdateSession>['updateSession']
}
const SessionSettingsTab: FC<Props> = ({ session, update }) => {
const { t } = useTranslation()
const onMoreSetting = () => {
if (session?.id) {
SessionSettingsPopup.show({
agentId: session.agent_id,
sessionId: session.id
})
}
}
if (!session) {
return null
}
return (
<div className="w-[var(--assistants-width)] p-2 px-3 pt-4">
<EssentialSettings agentBase={session} update={update} showModelSetting={false} />
<SettingDivider />
<AdvancedSettings agentBase={session} update={update} />
<SettingDivider />
<Button size="small" block onClick={onMoreSetting}>
{t('settings.moresetting.label')}
</Button>
</div>
)
}
export default SessionSettingsTab

View File

@ -1,24 +1,17 @@
import EditableNumber from '@renderer/components/EditableNumber' import EditableNumber from '@renderer/components/EditableNumber'
import { HStack } from '@renderer/components/Layout'
import Scrollbar from '@renderer/components/Scrollbar' import Scrollbar from '@renderer/components/Scrollbar'
import Selector from '@renderer/components/Selector' import Selector from '@renderer/components/Selector'
import { HelpTooltip } from '@renderer/components/TooltipIcons' import { HelpTooltip } from '@renderer/components/TooltipIcons'
import {
DEFAULT_CONTEXTCOUNT,
DEFAULT_MAX_TOKENS,
DEFAULT_TEMPERATURE,
MAX_CONTEXT_COUNT
} from '@renderer/config/constant'
import { isOpenAIModel, isSupportVerbosityModel } from '@renderer/config/models' import { isOpenAIModel, isSupportVerbosityModel } from '@renderer/config/models'
import { UNKNOWN } from '@renderer/config/translate' import { UNKNOWN } from '@renderer/config/translate'
import { useCodeStyle } from '@renderer/context/CodeStyleProvider' import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { useProvider } from '@renderer/hooks/useProvider' import { useProvider } from '@renderer/hooks/useProvider'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import useTranslate from '@renderer/hooks/useTranslate' import useTranslate from '@renderer/hooks/useTranslate'
import { SettingDivider, SettingRow, SettingRowTitle } from '@renderer/pages/settings' import { SettingDivider, SettingRow, SettingRowTitle } from '@renderer/pages/settings'
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
import { CollapsibleSettingGroup } from '@renderer/pages/settings/SettingGroup' import { CollapsibleSettingGroup } from '@renderer/pages/settings/SettingGroup'
import { getDefaultModel } from '@renderer/services/AssistantService' import { getDefaultModel } from '@renderer/services/AssistantService'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
@ -52,19 +45,17 @@ import {
setShowTranslateConfirm, setShowTranslateConfirm,
setThoughtAutoCollapse setThoughtAutoCollapse
} from '@renderer/store/settings' } from '@renderer/store/settings'
import type { Assistant, AssistantSettings, CodeStyleVarious, MathEngine } from '@renderer/types' import type { Assistant, CodeStyleVarious, MathEngine } from '@renderer/types'
import { isGroqSystemProvider, ThemeMode } from '@renderer/types' import { isGroqSystemProvider, ThemeMode } from '@renderer/types'
import { modalConfirm } from '@renderer/utils'
import { getSendMessageShortcutLabel } from '@renderer/utils/input' import { getSendMessageShortcutLabel } from '@renderer/utils/input'
import { import {
isOpenAICompatibleProvider, isOpenAICompatibleProvider,
isSupportServiceTierProvider, isSupportServiceTierProvider,
isSupportVerbosityProvider isSupportVerbosityProvider
} from '@renderer/utils/provider' } from '@renderer/utils/provider'
import { Button, Col, InputNumber, Row, Slider, Switch } from 'antd' import { Col, Row, Slider, Switch } from 'antd'
import { Settings2 } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
@ -76,20 +67,15 @@ interface Props {
} }
const SettingsTab: FC<Props> = (props) => { const SettingsTab: FC<Props> = (props) => {
const { assistant, updateAssistantSettings } = useAssistant(props.assistant.id) const { chat } = useRuntime()
const { assistant } = useAssistant(props.assistant.id)
const { provider } = useProvider(assistant.model.provider) const { provider } = useProvider(assistant.model.provider)
const { messageStyle, fontSize, language } = useSettings() const { messageStyle, fontSize, language } = useSettings()
const { theme } = useTheme() const { theme } = useTheme()
const { themeNames } = useCodeStyle() const { themeNames } = useCodeStyle()
const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
const [enableTemperature, setEnableTemperature] = useState(assistant?.settings?.enableTemperature ?? true)
const [contextCount, setContextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
const [enableMaxTokens, setEnableMaxTokens] = useState(assistant?.settings?.enableMaxTokens ?? false)
const [maxTokens, setMaxTokens] = useState(assistant?.settings?.maxTokens ?? 0)
const [fontSizeValue, setFontSizeValue] = useState(fontSize) const [fontSizeValue, setFontSizeValue] = useState(fontSize)
const [streamOutput, setStreamOutput] = useState(assistant?.settings?.streamOutput)
const { translateLanguages } = useTranslate() const { translateLanguages } = useTranslate()
const { t } = useTranslation() const { t } = useTranslation()
@ -128,28 +114,6 @@ const SettingsTab: FC<Props> = (props) => {
confirmRegenerateMessage confirmRegenerateMessage
} = useSettings() } = useSettings()
const onUpdateAssistantSettings = (settings: Partial<AssistantSettings>) => {
updateAssistantSettings(settings)
}
const onTemperatureChange = (value) => {
if (!isNaN(value as number)) {
onUpdateAssistantSettings({ temperature: value })
}
}
const onContextCountChange = (value) => {
if (!isNaN(value as number)) {
onUpdateAssistantSettings({ contextCount: value })
}
}
const onMaxTokensChange = (value) => {
if (!isNaN(value as number)) {
onUpdateAssistantSettings({ maxTokens: value })
}
}
const codeStyle = useMemo(() => { const codeStyle = useMemo(() => {
return codeEditor.enabled return codeEditor.enabled
? theme === ThemeMode.light ? theme === ThemeMode.light
@ -176,15 +140,6 @@ const SettingsTab: FC<Props> = (props) => {
[dispatch, theme, codeEditor.enabled] [dispatch, theme, codeEditor.enabled]
) )
useEffect(() => {
setTemperature(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
setEnableTemperature(assistant?.settings?.enableTemperature ?? true)
setContextCount(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
setEnableMaxTokens(assistant?.settings?.enableMaxTokens ?? false)
setMaxTokens(assistant?.settings?.maxTokens ?? DEFAULT_MAX_TOKENS)
setStreamOutput(assistant?.settings?.streamOutput ?? true)
}, [assistant])
const model = assistant.model || getDefaultModel() const model = assistant.model || getDefaultModel()
const showOpenAiSettings = const showOpenAiSettings =
@ -193,173 +148,36 @@ const SettingsTab: FC<Props> = (props) => {
isSupportServiceTierProvider(provider) || isSupportServiceTierProvider(provider) ||
(isSupportVerbosityModel(model) && isSupportVerbosityProvider(provider)) (isSupportVerbosityModel(model) && isSupportVerbosityProvider(provider))
const isTopicSettings = chat.activeTopicOrSession === 'topic'
return ( return (
<Container className="settings-tab"> <Container className="settings-tab">
{props.assistant.id !== 'fake' && ( {isTopicSettings && (
<CollapsibleSettingGroup <>
title={t('assistants.settings.title')} {showOpenAiSettings && (
defaultExpanded={true} <OpenAISettingsGroup
extra={ model={model}
<HStack alignItems="center" gap={2}> providerId={provider.id}
<Button SettingGroup={SettingGroup}
type="text" SettingRowTitleSmall={SettingRowTitleSmall}
size="small" />
icon={<Settings2 size={16} />} )}
onClick={() => AssistantSettingsPopup.show({ assistant, tab: 'model' })} {isGroqSystemProvider(provider) && (
/> <GroqSettingsGroup SettingGroup={SettingGroup} SettingRowTitleSmall={SettingRowTitleSmall} />
</HStack> )}
}> </>
<SettingGroup style={{ marginTop: 5 }}>
<Row align="middle">
<SettingRowTitleSmall>
{t('chat.settings.temperature.label')}
<HelpTooltip title={t('chat.settings.temperature.tip')} />
</SettingRowTitleSmall>
<Switch
size="small"
style={{ marginLeft: 'auto' }}
checked={enableTemperature}
onChange={(enabled) => {
setEnableTemperature(enabled)
onUpdateAssistantSettings({ enableTemperature: enabled })
}}
/>
</Row>
{enableTemperature ? (
<Row align="middle" gutter={10}>
<Col span={23}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
step={0.1}
/>
</Col>
</Row>
) : (
<SettingDivider />
)}
<Row align="middle" gutter={10} justify="space-between">
<SettingRowTitleSmall>
{t('chat.settings.context_count.label')}
<HelpTooltip title={t('chat.settings.context_count.tip')} />
</SettingRowTitleSmall>
<Col span={8}>
<EditableNumber
min={0}
max={20}
step={1}
value={contextCount}
changeOnBlur
onChange={(value) => {
if (value !== null && value >= 0 && value <= 20) {
setContextCount(value)
onContextCountChange(value)
}
}}
formatter={(value) => (value === MAX_CONTEXT_COUNT ? t('chat.settings.max') : (value ?? ''))}
style={{ width: '100%' }}
/>
</Col>
</Row>
<Row align="middle" gutter={10}>
<Col span={24}>
<Slider
min={0}
max={20}
onChange={setContextCount}
onChangeComplete={onContextCountChange}
value={Math.min(contextCount, 20)}
tooltip={{ open: false }}
step={1}
marks={{
0: '0',
10: '10',
20: '20'
}}
/>
</Col>
</Row>
<SettingDivider />
<SettingRow>
<SettingRowTitleSmall>{t('models.stream_output')}</SettingRowTitleSmall>
<Switch
size="small"
checked={streamOutput}
onChange={(checked) => {
setStreamOutput(checked)
onUpdateAssistantSettings({ streamOutput: checked })
}}
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<Row align="middle">
<SettingRowTitleSmall>
{t('chat.settings.max_tokens.label')}
<HelpTooltip title={t('chat.settings.max_tokens.tip')} />
</SettingRowTitleSmall>
</Row>
<Switch
size="small"
checked={enableMaxTokens}
onChange={async (enabled) => {
if (enabled) {
const confirmed = await modalConfirm({
title: t('chat.settings.max_tokens.confirm'),
content: t('chat.settings.max_tokens.confirm_content'),
okButtonProps: {
danger: true
}
})
if (!confirmed) return
}
setEnableMaxTokens(enabled)
onUpdateAssistantSettings({ enableMaxTokens: enabled })
}}
/>
</SettingRow>
{enableMaxTokens && (
<Row align="middle" gutter={10} style={{ marginTop: 10 }}>
<Col span={24}>
<InputNumber
disabled={!enableMaxTokens}
min={0}
max={10000000}
step={100}
value={typeof maxTokens === 'number' ? maxTokens : 0}
changeOnBlur
onChange={(value) => value && setMaxTokens(value)}
onBlur={() => onMaxTokensChange(maxTokens)}
style={{ width: '100%' }}
/>
</Col>
</Row>
)}
<SettingDivider />
</SettingGroup>
</CollapsibleSettingGroup>
)}
{showOpenAiSettings && (
<OpenAISettingsGroup
model={model}
providerId={provider.id}
SettingGroup={SettingGroup}
SettingRowTitleSmall={SettingRowTitleSmall}
/>
)}
{isGroqSystemProvider(provider) && (
<GroqSettingsGroup SettingGroup={SettingGroup} SettingRowTitleSmall={SettingRowTitleSmall} />
)} )}
<CollapsibleSettingGroup title={t('settings.messages.title')} defaultExpanded={true}> <CollapsibleSettingGroup title={t('settings.messages.title')} defaultExpanded={true}>
<SettingGroup> <SettingGroup>
<SettingRow> {isTopicSettings && (
<SettingRowTitleSmall>{t('settings.messages.prompt')}</SettingRowTitleSmall> <>
<Switch size="small" checked={showPrompt} onChange={(checked) => dispatch(setShowPrompt(checked))} /> <SettingRow>
</SettingRow> <SettingRowTitleSmall>{t('settings.messages.prompt')}</SettingRowTitleSmall>
<SettingDivider /> <Switch size="small" checked={showPrompt} onChange={(checked) => dispatch(setShowPrompt(checked))} />
</SettingRow>
<SettingDivider />
</>
)}
<SettingRow> <SettingRow>
<SettingRowTitleSmall>{t('settings.messages.use_serif_font')}</SettingRowTitleSmall> <SettingRowTitleSmall>{t('settings.messages.use_serif_font')}</SettingRowTitleSmall>
<Switch <Switch
@ -381,15 +199,19 @@ const SettingsTab: FC<Props> = (props) => {
/> />
</SettingRow> </SettingRow>
<SettingDivider /> <SettingDivider />
<SettingRow> {isTopicSettings && (
<SettingRowTitleSmall>{t('settings.messages.show_message_outline')}</SettingRowTitleSmall> <>
<Switch <SettingRow>
size="small" <SettingRowTitleSmall>{t('settings.messages.show_message_outline')}</SettingRowTitleSmall>
checked={showMessageOutline} <Switch
onChange={(checked) => dispatch(setShowMessageOutline(checked))} size="small"
/> checked={showMessageOutline}
</SettingRow> onChange={(checked) => dispatch(setShowMessageOutline(checked))}
<SettingDivider /> />
</SettingRow>
<SettingDivider />
</>
)}
<SettingRow> <SettingRow>
<SettingRowTitleSmall>{t('message.message.style.label')}</SettingRowTitleSmall> <SettingRowTitleSmall>{t('message.message.style.label')}</SettingRowTitleSmall>
<Selector <Selector
@ -402,20 +224,24 @@ const SettingsTab: FC<Props> = (props) => {
/> />
</SettingRow> </SettingRow>
<SettingDivider /> <SettingDivider />
<SettingRow> {isTopicSettings && (
<SettingRowTitleSmall>{t('message.message.multi_model_style.label')}</SettingRowTitleSmall> <>
<Selector <SettingRow>
value={multiModelMessageStyle} <SettingRowTitleSmall>{t('message.message.multi_model_style.label')}</SettingRowTitleSmall>
onChange={(value) => dispatch(setMultiModelMessageStyle(value))} <Selector
options={[ value={multiModelMessageStyle}
{ value: 'fold', label: t('message.message.multi_model_style.fold.label') }, onChange={(value) => dispatch(setMultiModelMessageStyle(value))}
{ value: 'vertical', label: t('message.message.multi_model_style.vertical') }, options={[
{ value: 'horizontal', label: t('message.message.multi_model_style.horizontal') }, { value: 'fold', label: t('message.message.multi_model_style.fold.label') },
{ value: 'grid', label: t('message.message.multi_model_style.grid') } { value: 'vertical', label: t('message.message.multi_model_style.vertical') },
]} { value: 'horizontal', label: t('message.message.multi_model_style.horizontal') },
/> { value: 'grid', label: t('message.message.multi_model_style.grid') }
</SettingRow> ]}
<SettingDivider /> />
</SettingRow>
<SettingDivider />
</>
)}
<SettingRow> <SettingRow>
<SettingRowTitleSmall>{t('settings.messages.navigation.label')}</SettingRowTitleSmall> <SettingRowTitleSmall>{t('settings.messages.navigation.label')}</SettingRowTitleSmall>
<Selector <Selector
@ -627,15 +453,19 @@ const SettingsTab: FC<Props> = (props) => {
</CollapsibleSettingGroup> </CollapsibleSettingGroup>
<CollapsibleSettingGroup title={t('settings.messages.input.title')} defaultExpanded={false}> <CollapsibleSettingGroup title={t('settings.messages.input.title')} defaultExpanded={false}>
<SettingGroup> <SettingGroup>
<SettingRow> {isTopicSettings && (
<SettingRowTitleSmall>{t('settings.messages.input.show_estimated_tokens')}</SettingRowTitleSmall> <>
<Switch <SettingRow>
size="small" <SettingRowTitleSmall>{t('settings.messages.input.show_estimated_tokens')}</SettingRowTitleSmall>
checked={showInputEstimatedTokens} <Switch
onChange={(checked) => dispatch(setShowInputEstimatedTokens(checked))} size="small"
/> checked={showInputEstimatedTokens}
</SettingRow> onChange={(checked) => dispatch(setShowInputEstimatedTokens(checked))}
<SettingDivider /> />
</SettingRow>
<SettingDivider />
</>
)}
<SettingRow> <SettingRow>
<SettingRowTitleSmall>{t('settings.messages.input.paste_long_text_as_file')}</SettingRowTitleSmall> <SettingRowTitleSmall>{t('settings.messages.input.paste_long_text_as_file')}</SettingRowTitleSmall>
<Switch <Switch

View File

@ -1,5 +1,4 @@
import { DeleteIcon, EditIcon } from '@renderer/components/Icons' import { DeleteIcon, EditIcon } from '@renderer/components/Icons'
import { useSessions } from '@renderer/hooks/agents/useSessions'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import AgentSettingsPopup from '@renderer/pages/settings/AgentSettings/AgentSettingsPopup' import AgentSettingsPopup from '@renderer/pages/settings/AgentSettings/AgentSettingsPopup'
import { AgentLabel } from '@renderer/pages/settings/AgentSettings/shared' import { AgentLabel } from '@renderer/pages/settings/AgentSettings/shared'
@ -8,7 +7,7 @@ import type { AgentEntity } from '@renderer/types'
import { cn } from '@renderer/utils' import { cn } from '@renderer/utils'
import type { MenuProps } from 'antd' import type { MenuProps } from 'antd'
import { Dropdown, Tooltip } from 'antd' import { Dropdown, Tooltip } from 'antd'
import { Bot } from 'lucide-react' import { Bot, MoreVertical } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
import { memo, useCallback, useMemo } from 'react' import { memo, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -24,8 +23,7 @@ interface AgentItemProps {
const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete, onPress }) => { const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete, onPress }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { sessions } = useSessions(agent.id) const { clickAssistantToShowTopic, topicPosition, assistantIconType } = useSettings()
const { clickAssistantToShowTopic, topicPosition } = useSettings()
const handlePress = useCallback(() => { const handlePress = useCallback(() => {
// Show session sidebar if setting is enabled (reusing the assistant setting for consistency) // Show session sidebar if setting is enabled (reusing the assistant setting for consistency)
@ -37,6 +35,14 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete, onPress }) =
onPress() onPress()
}, [clickAssistantToShowTopic, topicPosition, onPress]) }, [clickAssistantToShowTopic, topicPosition, onPress])
const handleMoreClick = useCallback(
(e: React.MouseEvent) => {
e.stopPropagation()
agent.id && AgentSettingsPopup.show({ agentId: agent.id })
},
[agent.id]
)
const menuItems: MenuProps['items'] = useMemo( const menuItems: MenuProps['items'] = useMemo(
() => [ () => [
{ {
@ -72,14 +78,14 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete, onPress }) =
<Container onClick={handlePress} isActive={isActive}> <Container onClick={handlePress} isActive={isActive}>
<AssistantNameRow className="name" title={agent.name ?? agent.id}> <AssistantNameRow className="name" title={agent.name ?? agent.id}>
<AgentNameWrapper> <AgentNameWrapper>
<AgentLabel agent={agent} /> <AgentLabel agent={agent} hideIcon={assistantIconType === 'none'} />
</AgentNameWrapper> </AgentNameWrapper>
{isActive && ( {isActive && (
<MenuButton> <MenuButton onClick={handleMoreClick}>
<SessionCount>{sessions.length}</SessionCount> <MoreVertical size={14} className="text-[var(--color-text-secondary)]" />
</MenuButton> </MenuButton>
)} )}
{!isActive && <BotIcon />} {!isActive && assistantIconType !== 'none' && <BotIcon />}
</AssistantNameRow> </AssistantNameRow>
</Container> </Container>
</Dropdown> </Dropdown>
@ -116,7 +122,7 @@ export const AgentNameWrapper: React.FC<React.HTMLAttributes<HTMLDivElement>> =
export const MenuButton: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ className, ...props }) => ( export const MenuButton: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ className, ...props }) => (
<div <div
className={cn( className={cn(
'flex h-5 min-h-5 w-5 flex-row items-center justify-center rounded-full border border-[var(--color-border)] bg-[var(--color-background)]', 'flex h-[22px] min-h-[22px] min-w-[22px] flex-row items-center justify-center rounded-[11px] border-[0.5px] border-[var(--color-border)] bg-[var(--color-background)] px-[5px]',
className className
)} )}
{...props} {...props}

View File

@ -22,6 +22,7 @@ import {
ArrowUpAZ, ArrowUpAZ,
BrushCleaning, BrushCleaning,
Check, Check,
MoreVertical,
Plus, Plus,
Save, Save,
Settings2, Settings2,
@ -150,6 +151,14 @@ const AssistantItem: FC<AssistantItemProps> = ({
[assistant.emoji, assistantName] [assistant.emoji, assistantName]
) )
const handleMoreClick = useCallback(
(e: React.MouseEvent) => {
e.stopPropagation()
AssistantSettingsPopup.show({ assistant })
},
[assistant]
)
return ( return (
<Dropdown <Dropdown
menu={{ items: menuItems }} menu={{ items: menuItems }}
@ -174,8 +183,8 @@ const AssistantItem: FC<AssistantItemProps> = ({
<AssistantName className="text-nowrap">{assistantName}</AssistantName> <AssistantName className="text-nowrap">{assistantName}</AssistantName>
</AssistantNameRow> </AssistantNameRow>
{isActive && ( {isActive && (
<MenuButton onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}> <MenuButton onClick={handleMoreClick}>
<TopicCount className="topics-count">{assistant.topics.length}</TopicCount> <MoreVertical size={14} className="text-[var(--color-text-secondary)]" />
</MenuButton> </MenuButton>
)} )}
</Container> </Container>
@ -447,19 +456,4 @@ const MenuButton = ({
</div> </div>
) )
const TopicCount = ({
children,
className,
...props
}: PropsWithChildren<{} & React.HTMLAttributes<HTMLDivElement>>) => (
<div
{...props}
className={cn(
'flex flex-row items-center justify-center rounded-[10px] text-[10px] text-[var(--color-text)]',
className
)}>
{children}
</div>
)
export default memo(AssistantItem) export default memo(AssistantItem)

View File

@ -232,12 +232,11 @@ const SessionListItem = styled.div`
} }
&.singlealone { &.singlealone {
border-radius: 0 !important;
&:hover { &:hover {
background-color: var(--color-background-soft); background-color: var(--color-background-soft);
} }
&.active { &.active {
border-left: 2px solid var(--color-primary); background-color: var(--color-background-mute);
box-shadow: none; box-shadow: none;
} }
} }

View File

@ -19,8 +19,6 @@ import styled from 'styled-components'
import AddButton from './AddButton' import AddButton from './AddButton'
import SessionItem from './SessionItem' import SessionItem from './SessionItem'
// const logger = loggerService.withContext('SessionsTab')
interface SessionsProps { interface SessionsProps {
agentId: string agentId: string
} }

View File

@ -499,7 +499,7 @@ export const Topics: React.FC<Props> = ({ assistant: _assistant, activeTopic, se
className="topics-tab" className="topics-tab"
list={sortedTopics} list={sortedTopics}
onUpdate={updateTopics} onUpdate={updateTopics}
style={{ height: '100%', padding: '11px 0 10px 10px' }} style={{ height: '100%', padding: '9px 0 10px 10px' }}
itemContainerStyle={{ paddingBottom: '8px' }} itemContainerStyle={{ paddingBottom: '8px' }}
header={ header={
<> <>
@ -632,12 +632,11 @@ const TopicListItem = styled.div`
} }
} }
&.singlealone { &.singlealone {
border-radius: 0 !important;
&:hover { &:hover {
background-color: var(--color-background-soft); background-color: var(--color-background-soft);
} }
&.active { &.active {
border-left: 2px solid var(--color-primary); background-color: var(--color-background-mute);
box-shadow: none; box-shadow: none;
} }
} }

View File

@ -1,8 +1,5 @@
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup' import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
import { useActiveSession } from '@renderer/hooks/agents/useActiveSession'
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings' import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useShowTopics } from '@renderer/hooks/useStore' import { useShowTopics } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
@ -10,16 +7,13 @@ import { useAppDispatch } from '@renderer/store'
import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store/runtime' import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store/runtime'
import type { Assistant, Topic } from '@renderer/types' import type { Assistant, Topic } from '@renderer/types'
import type { Tab } from '@renderer/types/chat' import type { Tab } from '@renderer/types/chat'
import { classNames, getErrorMessage, uuid } from '@renderer/utils' import { classNames, uuid } from '@renderer/utils'
import { Alert, Skeleton } from 'antd'
import type { FC } from 'react' import type { FC } from 'react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import Assistants from './AssistantsTab' import Assistants from './AssistantsTab'
import SessionSettingsTab from './SessionSettingsTab'
import Settings from './SettingsTab'
import Topics from './TopicsTab' import Topics from './TopicsTab'
interface Props { interface Props {
@ -49,15 +43,8 @@ const HomeTabs: FC<Props> = ({
const { toggleShowTopics } = useShowTopics() const { toggleShowTopics } = useShowTopics()
const { isLeftNavbar } = useNavbarPosition() const { isLeftNavbar } = useNavbarPosition()
const { t } = useTranslation() const { t } = useTranslation()
const { chat } = useRuntime()
const { activeTopicOrSession, activeAgentId } = chat
const { session, isLoading: isSessionLoading, error: sessionError } = useActiveSession()
const { updateSession } = useUpdateSession(activeAgentId)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const isSessionView = activeTopicOrSession === 'session'
const isTopicView = activeTopicOrSession === 'topic'
const [tab, setTab] = useState<Tab>(position === 'left' ? _tab || 'assistants' : 'topic') const [tab, setTab] = useState<Tab>(position === 'left' ? _tab || 'assistants' : 'topic')
const borderStyle = '0.5px solid var(--color-border)' const borderStyle = '0.5px solid var(--color-border)'
const border = const border =
@ -96,9 +83,6 @@ const HomeTabs: FC<Props> = ({
EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => { EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => {
showTab && setTab('topic') showTab && setTab('topic')
}), }),
EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => {
showTab && setTab('settings')
}),
EventEmitter.on(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR, () => { EventEmitter.on(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR, () => {
showTab && setTab('topic') showTab && setTab('topic')
if (position === 'left' && topicPosition === 'right') { if (position === 'left' && topicPosition === 'right') {
@ -113,7 +97,7 @@ const HomeTabs: FC<Props> = ({
if (position === 'right' && topicPosition === 'right' && tab === 'assistants') { if (position === 'right' && topicPosition === 'right' && tab === 'assistants') {
setTab('topic') setTab('topic')
} }
if (position === 'left' && topicPosition === 'right' && (tab === 'topic' || tab === 'settings')) { if (position === 'left' && topicPosition === 'right' && tab === 'topic') {
setTab('assistants') setTab('assistants')
} }
}, [position, tab, topicPosition, forceToSeeAllTab]) }, [position, tab, topicPosition, forceToSeeAllTab])
@ -130,20 +114,6 @@ const HomeTabs: FC<Props> = ({
<TabItem active={tab === 'topic'} onClick={() => setTab('topic')}> <TabItem active={tab === 'topic'} onClick={() => setTab('topic')}>
{t('common.topics')} {t('common.topics')}
</TabItem> </TabItem>
<TabItem active={tab === 'settings'} onClick={() => setTab('settings')}>
{t('settings.title')}
</TabItem>
</CustomTabs>
)}
{position === 'right' && topicPosition === 'right' && (
<CustomTabs>
<TabItem active={tab === 'topic'} onClick={() => setTab('topic')}>
{t('common.topics')}
</TabItem>
<TabItem active={tab === 'settings'} onClick={() => setTab('settings')}>
{t('settings.title')}
</TabItem>
</CustomTabs> </CustomTabs>
)} )}
@ -164,22 +134,6 @@ const HomeTabs: FC<Props> = ({
position={position} position={position}
/> />
)} )}
{tab === 'settings' && isTopicView && <Settings assistant={activeAssistant} />}
{tab === 'settings' && isSessionView && !sessionError && (
<Skeleton loading={isSessionLoading} active style={{ height: '100%', padding: '16px' }}>
<SessionSettingsTab session={session} update={updateSession} />
</Skeleton>
)}
{tab === 'settings' && isSessionView && sessionError && (
<div className="w-[var(--assistants-width)] p-2 px-3 pt-4">
<Alert
type="error"
message={t('agent.session.get.error.failed')}
description={getErrorMessage(sessionError)}
style={{ padding: '10px 15px' }}
/>
</div>
)}
</TabContent> </TabContent>
</Container> </Container>
) )

View File

@ -1,14 +1,17 @@
import EmojiIcon from '@renderer/components/EmojiIcon'
import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer' import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer'
import { useActiveAgent } from '@renderer/hooks/agents/useActiveAgent' import { useActiveAgent } from '@renderer/hooks/agents/useActiveAgent'
import { useActiveSession } from '@renderer/hooks/agents/useActiveSession' import { useActiveSession } from '@renderer/hooks/agents/useActiveSession'
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession' import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
import { useRuntime } from '@renderer/hooks/useRuntime' import { useRuntime } from '@renderer/hooks/useRuntime'
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
import type { AgentEntity, AgentSessionEntity, ApiModel, Assistant } from '@renderer/types' import type { AgentEntity, AgentSessionEntity, ApiModel, Assistant } from '@renderer/types'
import { getLeadingEmoji } from '@renderer/utils'
import { formatErrorMessageWithPrefix } from '@renderer/utils/error' import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
import { t } from 'i18next' import { t } from 'i18next'
import { ChevronRight, Folder } from 'lucide-react' import { ChevronRight, Folder } from 'lucide-react'
import type { FC, ReactNode } from 'react' import type { FC, ReactNode } from 'react'
import { useCallback } from 'react' import { useCallback, useMemo } from 'react'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { AgentSettingsPopup, SessionSettingsPopup } from '../../settings/AgentSettings' import { AgentSettingsPopup, SessionSettingsPopup } from '../../settings/AgentSettings'
@ -29,6 +32,8 @@ const ChatNavbarContent: FC<Props> = ({ assistant }) => {
const { session: activeSession } = useActiveSession() const { session: activeSession } = useActiveSession()
const { updateModel } = useUpdateSession(activeAgent?.id ?? null) const { updateModel } = useUpdateSession(activeAgent?.id ?? null)
const assistantName = useMemo(() => assistant.name || t('chat.default.name'), [assistant.name])
const handleUpdateModel = useCallback( const handleUpdateModel = useCallback(
async (model: ApiModel) => { async (model: ApiModel) => {
if (!activeAgent || !activeSession) return if (!activeAgent || !activeSession) return
@ -39,7 +44,25 @@ const ChatNavbarContent: FC<Props> = ({ assistant }) => {
return ( return (
<> <>
{activeTopicOrSession === 'topic' && <SelectModelButton assistant={assistant} />} {activeTopicOrSession === 'topic' && (
<HorizontalScrollContainer className="ml-2 flex-initial">
<div className="flex flex-nowrap items-center gap-2">
{/* Assistant Label */}
<div
className="flex h-full cursor-pointer items-center gap-1.5"
onClick={() => AssistantSettingsPopup.show({ assistant })}>
<EmojiIcon emoji={assistant.emoji || getLeadingEmoji(assistantName)} size={24} />
<span className="max-w-40 truncate text-xs">{assistantName}</span>
</div>
{/* Separator */}
<ChevronRight className="h-4 w-4 text-gray-400" />
{/* Model Button */}
<SelectModelButton assistant={assistant} />
</div>
</HorizontalScrollContainer>
)}
{activeTopicOrSession === 'session' && activeAgent && ( {activeTopicOrSession === 'session' && activeAgent && (
<HorizontalScrollContainer className="ml-2 flex-initial"> <HorizontalScrollContainer className="ml-2 flex-initial">
<div className="flex flex-nowrap items-center gap-2"> <div className="flex flex-nowrap items-center gap-2">

View File

@ -0,0 +1,38 @@
import type { Assistant } from '@renderer/types'
import { Drawer, Tooltip } from 'antd'
import { t } from 'i18next'
import { Settings2 } from 'lucide-react'
import type { FC } from 'react'
import { useState } from 'react'
import { NavbarIcon } from '../ChatNavbar'
import HomeSettings from '../Tabs/SettingsTab'
interface Props {
assistant: Assistant
}
const SettingsButton: FC<Props> = ({ assistant }) => {
const [settingsOpen, setSettingsOpen] = useState(false)
return (
<>
<Tooltip title={t('settings.title')} mouseEnterDelay={0.8}>
<NavbarIcon onClick={() => setSettingsOpen(true)}>
<Settings2 size={18} />
</NavbarIcon>
</Tooltip>
<Drawer
placement="right"
open={settingsOpen}
onClose={() => setSettingsOpen(false)}
width="var(--assistants-width)"
closable={false}
styles={{ body: { padding: 0, paddingTop: 'var(--navbar-height)' } }}>
<HomeSettings assistant={assistant} />
</Drawer>
</>
)
}
export default SettingsButton

View File

@ -119,7 +119,7 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
onOk={onOk} onOk={onOk}
onCancel={onCancel} onCancel={onCancel}
afterClose={afterClose} afterClose={afterClose}
maskClosable={false} maskClosable={menu !== 'prompt'}
footer={null} footer={null}
title={<AgentLabel agent={agent} />} title={<AgentLabel agent={agent} />}
transitionName="animation-move-down" transitionName="animation-move-down"

View File

@ -29,14 +29,15 @@ export type AgentLabelProps = {
avatar?: string avatar?: string
name?: string name?: string
} }
hideIcon?: boolean
} }
export const AgentLabel: React.FC<AgentLabelProps> = ({ agent, classNames }) => { export const AgentLabel: React.FC<AgentLabelProps> = ({ agent, classNames, hideIcon }) => {
const emoji = agent?.configuration?.avatar const emoji = agent?.configuration?.avatar
return ( return (
<div className={cn('flex w-full items-center gap-2 truncate', classNames?.container)}> <div className={cn('flex w-full items-center gap-2 truncate', classNames?.container)}>
<EmojiIcon emoji={emoji || '⭐️'} className={classNames?.avatar} /> {!hideIcon && <EmojiIcon emoji={emoji || '⭐️'} className={classNames?.avatar} size={24} />}
<span className={cn('truncate', 'text-[var(--color-text)]', classNames?.name)}> <span className={cn('truncate', 'text-[var(--color-text)]', classNames?.name)}>
{agent?.name ?? (agent?.type ? getAgentTypeLabel(agent.type) : '')} {agent?.name ?? (agent?.type ? getAgentTypeLabel(agent.type) : '')}
</span> </span>

View File

@ -37,7 +37,7 @@ interface Props extends AssistantSettingPopupShowParams {
const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...props }) => { const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...props }) => {
const [open, setOpen] = useState(true) const [open, setOpen] = useState(true)
const { t } = useTranslation() const { t } = useTranslation()
const [menu, setMenu] = useState<AssistantSettingPopupTab>(tab || 'prompt') const [menu, setMenu] = useState<AssistantSettingPopupTab>(tab || 'model')
const _useAssistant = useAssistant(props.assistant.id) const _useAssistant = useAssistant(props.assistant.id)
const _useAgent = useAssistantPreset(props.assistant.id) const _useAgent = useAssistantPreset(props.assistant.id)
@ -64,14 +64,14 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
} }
const items = [ const items = [
{
key: 'prompt',
label: t('assistants.settings.prompt')
},
{ {
key: 'model', key: 'model',
label: t('assistants.settings.model') label: t('assistants.settings.model')
}, },
{
key: 'prompt',
label: t('assistants.settings.prompt')
},
showKnowledgeIcon && { showKnowledgeIcon && {
key: 'knowledge_base', key: 'knowledge_base',
label: t('assistants.settings.knowledge_base.label') label: t('assistants.settings.knowledge_base.label')
@ -96,7 +96,7 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
onOk={onOk} onOk={onOk}
onCancel={onCancel} onCancel={onCancel}
afterClose={afterClose} afterClose={afterClose}
maskClosable={false} maskClosable={menu !== 'prompt'}
footer={null} footer={null}
title={assistant.name} title={assistant.name}
transitionName="animation-move-down" transitionName="animation-move-down"
@ -116,22 +116,22 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
<HStack> <HStack>
<LeftMenu> <LeftMenu>
<StyledMenu <StyledMenu
defaultSelectedKeys={[tab || 'prompt']} defaultSelectedKeys={[tab || 'model']}
mode="vertical" mode="vertical"
items={items} items={items}
onSelect={({ key }) => setMenu(key as AssistantSettingPopupTab)} onSelect={({ key }) => setMenu(key as AssistantSettingPopupTab)}
/> />
</LeftMenu> </LeftMenu>
<Settings> <Settings>
{menu === 'prompt' && ( {menu === 'model' && (
<AssistantPromptSettings <AssistantModelSettings
assistant={assistant} assistant={assistant}
updateAssistant={updateAssistant} updateAssistant={updateAssistant}
updateAssistantSettings={updateAssistantSettings} updateAssistantSettings={updateAssistantSettings}
/> />
)} )}
{menu === 'model' && ( {menu === 'prompt' && (
<AssistantModelSettings <AssistantPromptSettings
assistant={assistant} assistant={assistant}
updateAssistant={updateAssistant} updateAssistant={updateAssistant}
updateAssistantSettings={updateAssistantSettings} updateAssistantSettings={updateAssistantSettings}

View File

@ -4,8 +4,6 @@ export const EventEmitter = new Emittery()
export const EVENT_NAMES = { export const EVENT_NAMES = {
SEND_MESSAGE: 'SEND_MESSAGE', SEND_MESSAGE: 'SEND_MESSAGE',
// APPEND_MESSAGE: 'APPEND_MESSAGE',
// RECEIVE_MESSAGE: 'RECEIVE_MESSAGE',
MESSAGE_COMPLETE: 'MESSAGE_COMPLETE', MESSAGE_COMPLETE: 'MESSAGE_COMPLETE',
AI_AUTO_RENAME: 'AI_AUTO_RENAME', AI_AUTO_RENAME: 'AI_AUTO_RENAME',
CLEAR_MESSAGES: 'CLEAR_MESSAGES', CLEAR_MESSAGES: 'CLEAR_MESSAGES',
@ -15,7 +13,6 @@ export const EVENT_NAMES = {
CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED', CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED',
ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT', ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT',
SHOW_ASSISTANTS: 'SHOW_ASSISTANTS', SHOW_ASSISTANTS: 'SHOW_ASSISTANTS',
SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS',
SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR', SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR',
SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR', SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR',
NEW_CONTEXT: 'NEW_CONTEXT', NEW_CONTEXT: 'NEW_CONTEXT',

View File

@ -1,4 +1,4 @@
export type Tab = 'assistants' | 'topic' | 'settings' export type Tab = 'assistants' | 'topic'
export type InputBarToolType = export type InputBarToolType =
| 'new_topic' | 'new_topic'