mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 21:01:32 +08:00
feat(agent): add permission mode display component for empty session state (#11204)
Replace empty state text with a visual permission mode display card that shows: - Permission mode icon with unique colors for each mode (default, plan, acceptEdits, bypassPermissions) - Permission mode title and description - Clickable to navigate directly to tooling settings tab Replace loading text with Ant Design Spin component for better UX.
This commit is contained in:
parent
e43562423e
commit
bc8b0a8d53
@ -5,11 +5,13 @@ import { useTopicMessages } from '@renderer/hooks/useMessageOperations'
|
||||
import { getGroupedMessages } from '@renderer/services/MessagesService'
|
||||
import { type Topic, TopicType } from '@renderer/types'
|
||||
import { buildAgentSessionTopicId } from '@renderer/utils/agentSession'
|
||||
import { Spin } from 'antd'
|
||||
import { memo, useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import MessageGroup from './MessageGroup'
|
||||
import NarrowLayout from './NarrowLayout'
|
||||
import PermissionModeDisplay from './PermissionModeDisplay'
|
||||
import { MessagesContainer, ScrollContainer } from './shared'
|
||||
|
||||
const logger = loggerService.withContext('AgentSessionMessages')
|
||||
@ -67,8 +69,12 @@ const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
|
||||
groupedMessages.map(([key, groupMessages]) => (
|
||||
<MessageGroup key={key} messages={groupMessages} topic={derivedTopic} />
|
||||
))
|
||||
) : session ? (
|
||||
<PermissionModeDisplay session={session} agentId={agentId} />
|
||||
) : (
|
||||
<EmptyState>{session ? 'No messages yet.' : 'Loading session...'}</EmptyState>
|
||||
<LoadingState>
|
||||
<Spin size="small" />
|
||||
</LoadingState>
|
||||
)}
|
||||
</ScrollContainer>
|
||||
</ContextMenu>
|
||||
@ -77,10 +83,10 @@ const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const EmptyState = styled.div`
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
const LoadingState = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
`
|
||||
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
import { permissionModeCards } from '@renderer/config/agent'
|
||||
import SessionSettingsPopup from '@renderer/pages/settings/AgentSettings/SessionSettingsPopup'
|
||||
import type { GetAgentSessionResponse, PermissionMode } from '@renderer/types'
|
||||
import { FileEdit, Lightbulb, Shield, ShieldOff } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface Props {
|
||||
session: GetAgentSessionResponse
|
||||
agentId: string
|
||||
}
|
||||
|
||||
const getPermissionModeConfig = (mode: PermissionMode) => {
|
||||
switch (mode) {
|
||||
case 'default':
|
||||
return {
|
||||
icon: <Shield size={18} color="var(--color-primary)" />
|
||||
}
|
||||
case 'plan':
|
||||
return {
|
||||
icon: <Lightbulb size={18} color="#faad14" />
|
||||
}
|
||||
case 'acceptEdits':
|
||||
return {
|
||||
icon: <FileEdit size={18} color="#52c41a" />
|
||||
}
|
||||
case 'bypassPermissions':
|
||||
return {
|
||||
icon: <ShieldOff size={18} color="var(--color-error)" />
|
||||
}
|
||||
default:
|
||||
return {
|
||||
icon: <Shield size={18} color="var(--color-primary)" />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PermissionModeDisplay: FC<Props> = ({ session, agentId }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const permissionMode = session?.configuration?.permission_mode ?? 'default'
|
||||
|
||||
const modeCard = useMemo(() => {
|
||||
return permissionModeCards.find((card) => card.mode === permissionMode)
|
||||
}, [permissionMode])
|
||||
|
||||
const modeConfig = useMemo(() => getPermissionModeConfig(permissionMode), [permissionMode])
|
||||
|
||||
const handleClick = () => {
|
||||
SessionSettingsPopup.show({
|
||||
agentId,
|
||||
sessionId: session.id,
|
||||
tab: 'tooling'
|
||||
})
|
||||
}
|
||||
|
||||
if (!modeCard) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={handleClick}
|
||||
className="mx-2 cursor-pointer rounded-lg border-[0.5px] border-[var(--color-border)] px-3 py-2">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="flex shrink-0 items-center justify-center">{modeConfig.icon}</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap font-semibold text-[var(--color-text-1)] text-xs">
|
||||
{t(modeCard.titleKey, modeCard.titleFallback)}
|
||||
</div>
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap text-[11px] text-[var(--color-text-2)] leading-[1.4]">
|
||||
{t(modeCard.descriptionKey, modeCard.descriptionFallback)}{' '}
|
||||
{t(modeCard.behaviorKey, modeCard.behaviorFallback)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PermissionModeDisplay
|
||||
Loading…
Reference in New Issue
Block a user