This commit is contained in:
kangfenmao 2025-11-03 18:39:39 +08:00
parent 3cc7ee01e2
commit 28fac543fc
22 changed files with 77 additions and 141 deletions

View File

@ -1,31 +0,0 @@
import { Avatar, cn } from '@heroui/react'
import { getModelLogoById } from '@renderer/config/models'
import type { ApiModel } from '@renderer/types'
import React from 'react'
import Ellipsis from './Ellipsis'
export interface ModelLabelProps extends Omit<React.ComponentPropsWithRef<'div'>, 'children'> {
model?: ApiModel
classNames?: {
container?: string
avatar?: string
modelName?: string
divider?: string
providerName?: string
}
}
export const ApiModelLabel: React.FC<ModelLabelProps> = ({ model, className, classNames, ...props }) => {
return (
<div className={cn('flex items-center gap-1', className, classNames?.container)} {...props}>
<Avatar
src={model ? (getModelLogoById(model.id) ?? getModelLogoById(model.name)) : undefined}
className={cn('h-4 w-4', classNames?.avatar)}
/>
<Ellipsis className={classNames?.modelName}>{model?.name}</Ellipsis>
<span className={classNames?.divider}> | </span>
<Ellipsis className={classNames?.providerName}>{model?.provider_name}</Ellipsis>
</div>
)
}

View File

@ -1,4 +1,4 @@
import { cn } from '@heroui/react'
import { cn } from '@renderer/utils'
import type { ButtonProps } from 'antd'
import { Button } from 'antd'
import React, { memo } from 'react'

View File

@ -1,5 +1,5 @@
import { cn } from '@heroui/react'
import Scrollbar from '@renderer/components/Scrollbar'
import { cn } from '@renderer/utils'
import { ChevronRight } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import styled from 'styled-components'

View File

@ -1,5 +1,5 @@
import { cn } from '@heroui/react'
import { TopView } from '@renderer/components/TopView'
import { cn } from '@renderer/utils'
import { Modal } from 'antd'
import { Bot, MessageSquare } from 'lucide-react'
import { useState } from 'react'

View File

@ -1,6 +1,5 @@
import {
Button,
cn,
Form,
Input,
Modal,
@ -25,6 +24,7 @@ import type {
Tool,
UpdateSessionForm
} from '@renderer/types'
import { cn } from '@renderer/utils'
import type { FormEvent, ReactNode } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -1,6 +1,7 @@
import type { SelectedItems, SelectProps } from '@heroui/react'
import { Chip, cn, Select, SelectItem } from '@heroui/react'
import { Chip, Select, SelectItem } from '@heroui/react'
import type { Tool } from '@renderer/types'
import { cn } from '@renderer/utils'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -12,19 +12,7 @@ export default function useUserTheme() {
const colorPrimary = Color(theme.colorPrimary)
document.body.style.setProperty('--color-primary', colorPrimary.toString())
// overwrite hero UI primary color.
document.body.style.setProperty('--primary', colorPrimary.toString())
document.body.style.setProperty('--heroui-primary', colorPrimary.toString())
document.body.style.setProperty('--heroui-primary-900', colorPrimary.lighten(0.5).toString())
document.body.style.setProperty('--heroui-primary-800', colorPrimary.lighten(0.4).toString())
document.body.style.setProperty('--heroui-primary-700', colorPrimary.lighten(0.3).toString())
document.body.style.setProperty('--heroui-primary-600', colorPrimary.lighten(0.2).toString())
document.body.style.setProperty('--heroui-primary-500', colorPrimary.lighten(0.1).toString())
document.body.style.setProperty('--heroui-primary-400', colorPrimary.toString())
document.body.style.setProperty('--heroui-primary-300', colorPrimary.darken(0.1).toString())
document.body.style.setProperty('--heroui-primary-200', colorPrimary.darken(0.2).toString())
document.body.style.setProperty('--heroui-primary-100', colorPrimary.darken(0.3).toString())
document.body.style.setProperty('--heroui-primary-50', colorPrimary.darken(0.4).toString())
document.body.style.setProperty('--color-primary-soft', colorPrimary.alpha(0.6).toString())
document.body.style.setProperty('--color-primary-mute', colorPrimary.alpha(0.3).toString())

View File

@ -1,4 +1,3 @@
import { Alert } from '@heroui/react'
import { loggerService } from '@logger'
import type { ContentSearchRef } from '@renderer/components/ContentSearch'
import { ContentSearch } from '@renderer/components/ContentSearch'
@ -17,7 +16,7 @@ import { useTimer } from '@renderer/hooks/useTimer'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import type { Assistant, Topic } from '@renderer/types'
import { classNames } from '@renderer/utils'
import { Flex } from 'antd'
import { Alert, Flex } from 'antd'
import { debounce } from 'lodash'
import { AnimatePresence, motion } from 'motion/react'
import type { FC } from 'react'
@ -170,11 +169,7 @@ const Chat: FC<Props> = (props) => {
return () => <div> Active Session ID is invalid.</div>
}
if (!apiServer.enabled) {
return () => (
<div>
<Alert color="warning" title={t('agent.warning.enable_server')} />
</div>
)
return () => <Alert type="warning" message={t('agent.warning.enable_server')} style={{ margin: '5px 16px' }} />
}
return () => <AgentSessionMessages agentId={activeAgentId} sessionId={activeSessionId} />
}, [activeAgentId, activeSessionId, apiServer.enabled, t])
@ -191,22 +186,14 @@ const Chat: FC<Props> = (props) => {
// TODO: more info
const AgentInvalid = useCallback(() => {
return (
<div className="flex h-full w-full items-center justify-center">
<div>
<Alert color="warning" title="Select an agent" />
</div>
</div>
)
return <Alert type="warning" message="Select an agent" style={{ margin: '5px 16px' }} />
}, [])
// TODO: more info
const SessionInvalid = useCallback(() => {
return (
<div className="flex h-full w-full items-center justify-center">
<div>
<Alert color="warning" title="Create a session" />
</div>
<Alert type="warning" message="Create a session" style={{ margin: '5px 16px' }} />
</div>
)
}, [])

View File

@ -1,4 +1,3 @@
import { cn } from '@heroui/react'
import { loggerService } from '@logger'
import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer'
import Scrollbar from '@renderer/components/Scrollbar'
@ -15,7 +14,7 @@ import { getModelUniqId } from '@renderer/services/ModelService'
import { estimateMessageUsage } from '@renderer/services/TokenService'
import type { Assistant, Topic } from '@renderer/types'
import type { Message, MessageBlock } from '@renderer/types/newMessage'
import { classNames } from '@renderer/utils'
import { classNames, cn } from '@renderer/utils'
import { isMessageProcessing } from '@renderer/utils/messageUtils/is'
import { Divider } from 'antd'
import type { Dispatch, FC, SetStateAction } from 'react'

View File

@ -1,9 +1,10 @@
import { Button, Divider } from '@heroui/react'
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'
@ -31,9 +32,10 @@ const SessionSettingsTab: FC<Props> = ({ session, update }) => {
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} />
<Divider className="my-2" />
<Button size="sm" fullWidth onPress={onMoreSetting}>
<SettingDivider />
<Button size="small" block onClick={onMoreSetting}>
{t('settings.moresetting.label')}
</Button>
</div>

View File

@ -1,6 +1,7 @@
import { Alert, cn } from '@heroui/react'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import { cn } from '@renderer/utils'
import { Alert } from 'antd'
import { AnimatePresence, motion } from 'framer-motion'
import type { FC } from 'react'
import { memo } from 'react'
@ -17,19 +18,11 @@ const SessionsTab: FC<SessionsTabProps> = () => {
const { apiServer } = useSettings()
if (!apiServer.enabled) {
return (
<div>
<Alert color="warning" title={t('agent.warning.enable_server')} />
</div>
)
return <Alert type="warning" message={t('agent.warning.enable_server')} style={{ margin: 10 }} />
}
if (!activeAgentId) {
return (
<div>
<Alert color="warning" title={'Select an agent'} />
</div>
)
return <Alert type="warning" message={'Select an agent'} style={{ margin: 10 }} />
}
return (

View File

@ -1,5 +1,6 @@
import type { ButtonProps } from '@heroui/react'
import { Button, cn } from '@heroui/react'
import { Button } from '@heroui/react'
import { cn } from '@renderer/utils'
import { PlusIcon } from 'lucide-react'
import type { FC } from 'react'

View File

@ -1,4 +1,3 @@
import { cn, Tooltip } from '@heroui/react'
import { DeleteIcon, EditIcon } from '@renderer/components/Icons'
import { useSessions } from '@renderer/hooks/agents/useSessions'
import { useSettings } from '@renderer/hooks/useSettings'
@ -7,6 +6,8 @@ import { AgentLabel } from '@renderer/pages/settings/AgentSettings/shared'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import type { AgentEntity } from '@renderer/types'
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@renderer/ui/context-menu'
import { cn } from '@renderer/utils'
import { Tooltip } from 'antd'
import { Bot } from 'lucide-react'
import type { FC } from 'react'
import { memo, useCallback } from 'react'
@ -118,7 +119,7 @@ export const MenuButton: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ cla
export const BotIcon: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ ...props }) => {
const { t } = useTranslation()
return (
<Tooltip content={t('common.agent_one')} delay={500} closeDelay={0}>
<Tooltip title={t('common.agent_one')} mouseEnterDelay={0.5}>
<MenuButton {...props}>
<Bot size={14} className="text-primary" />
</MenuButton>

View File

@ -1,4 +1,3 @@
import { cn } from '@heroui/react'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import EmojiIcon from '@renderer/components/EmojiIcon'
import { CopyIcon, DeleteIcon, EditIcon } from '@renderer/components/Icons'
@ -12,7 +11,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { useAppDispatch } from '@renderer/store'
import { setActiveTopicOrSessionAction } from '@renderer/store/runtime'
import type { Assistant, AssistantsSortType } from '@renderer/types'
import { getLeadingEmoji, uuid } from '@renderer/utils'
import { cn, getLeadingEmoji, uuid } from '@renderer/utils'
import { hasTopicPendingRequests } from '@renderer/utils/queue'
import type { MenuProps } from 'antd'
import { Dropdown } from 'antd'

View File

@ -1,5 +1,5 @@
import { DownOutlined, RightOutlined } from '@ant-design/icons'
import { cn } from '@heroui/react'
import { cn } from '@renderer/utils'
import { Tooltip } from 'antd'
import type { FC, ReactNode } from 'react'

View File

@ -1,4 +1,4 @@
import { Alert, Skeleton } from '@heroui/react'
import { Skeleton } from '@heroui/react'
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
import { useActiveSession } from '@renderer/hooks/agents/useActiveSession'
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
@ -12,6 +12,7 @@ import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store
import type { Assistant, Topic } from '@renderer/types'
import type { Tab } from '@renderer/types/chat'
import { classNames, getErrorMessage, uuid } from '@renderer/utils'
import { Alert } from 'antd'
import type { FC } from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -170,12 +171,13 @@ const HomeTabs: FC<Props> = ({
<SessionSettingsTab session={session} update={updateSession} />
</Skeleton>
)}
{tab === 'settings' && isSessionView && sessionError && (
{tab === 'settings' && (
<div className="w-[var(--assistants-width)] p-2 px-3 pt-4">
<Alert
color="danger"
title={t('agent.session.get.error.failed')}
type="error"
message={t('agent.session.get.error.failed')}
description={getErrorMessage(sessionError)}
style={{ padding: '10px 15px' }}
/>
</div>
)}

View File

@ -1,4 +1,3 @@
import { Button } from '@heroui/react'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import { SelectApiModelPopup } from '@renderer/components/Popups/SelectModelPopup'
import { agentModelFilter } from '@renderer/config/models'
@ -8,6 +7,7 @@ import type { AgentBaseWithId, ApiModel } from '@renderer/types'
import { isAgentEntity } from '@renderer/types'
import { getModelFilterByAgentType } from '@renderer/utils/agentSession'
import { apiModelAdapter } from '@renderer/utils/model'
import { Button } from 'antd'
import { ChevronsUpDown } from 'lucide-react'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
@ -37,11 +37,11 @@ const SelectAgentBaseModelButton: FC<Props> = ({ agentBase: agent, onSelect, isD
return (
<Button
size="sm"
variant="light"
className="nodrag h-[28px] rounded-2xl px-1"
onPress={onSelectModel}
isDisabled={isDisabled}>
size="small"
type="text"
style={{ borderRadius: 20, fontSize: 12, padding: 2 }}
onClick={onSelectModel}
disabled={isDisabled}>
<div className="flex items-center gap-1.5 overflow-x-hidden">
<ModelAvatar model={model ? apiModelAdapter(model) : undefined} size={20} />
<span className="truncate text-[var(--color-text)]">

View File

@ -1,5 +1,5 @@
import { Button, Input } from '@heroui/react'
import { loggerService } from '@logger'
import { Button, Input, InputRef } from 'antd'
import type { WebviewTag } from 'electron'
import { ChevronDown, ChevronUp, X } from 'lucide-react'
import type { FC } from 'react'
@ -22,7 +22,7 @@ const WebviewSearch: FC<WebviewSearchProps> = ({ webviewRef, isWebviewReady, app
const [query, setQuery] = useState('')
const [matchCount, setMatchCount] = useState(0)
const [activeIndex, setActiveIndex] = useState(0)
const inputRef = useRef<HTMLInputElement>(null)
const inputRef = useRef<InputRef>(null)
const focusFrameRef = useRef<number | null>(null)
const lastAppIdRef = useRef<string>(appId)
const attachedWebviewRef = useRef<WebviewTag | null>(null)
@ -315,19 +315,13 @@ const WebviewSearch: FC<WebviewSearchProps> = ({ webviewRef, isWebviewReady, app
ref={inputRef}
autoFocus
value={query}
onValueChange={setQuery}
spellCheck={'false'}
onChange={(e) => setQuery(e.target.value)}
spellCheck={false}
placeholder={t('common.search')}
size="sm"
radius="sm"
variant="flat"
classNames={{
base: 'w-[240px]',
inputWrapper:
'h-8 bg-transparent border border-transparent shadow-none hover:border-transparent hover:bg-transparent focus:border-transparent data-[hover=true]:border-transparent data-[focus=true]:border-transparent data-[focus-visible=true]:outline-none data-[focus-visible=true]:ring-0',
input: 'text-small focus:outline-none focus-visible:outline-none',
innerWrapper: 'gap-0'
}}
size="small"
variant="borderless"
className="w-[240px]"
style={{ height: '32px' }}
/>
<span
className="min-w-[44px] text-center text-default-500 text-small tabular-nums"
@ -339,38 +333,32 @@ const WebviewSearch: FC<WebviewSearchProps> = ({ webviewRef, isWebviewReady, app
</span>
<div className="h-4 w-px bg-default-200" />
<Button
size="sm"
variant="light"
radius="full"
isIconOnly
onPress={goToPrevious}
isDisabled={disableNavigation}
size="small"
type="text"
onClick={goToPrevious}
disabled={disableNavigation}
aria-label="Previous match"
className="text-default-500 hover:text-default-900">
<ChevronUp size={16} />
</Button>
icon={<ChevronUp size={16} className="w-6" />}
className="text-default-500 hover:text-default-900"
/>
<Button
size="sm"
variant="light"
radius="full"
isIconOnly
onPress={goToNext}
isDisabled={disableNavigation}
size="small"
type="text"
onClick={goToNext}
disabled={disableNavigation}
aria-label="Next match"
className="text-default-500 hover:text-default-900">
<ChevronDown size={16} />
</Button>
icon={<ChevronDown size={16} className="w-6" />}
className="text-default-500 hover:text-default-900"
/>
<div className="h-4 w-px bg-default-200" />
<Button
size="sm"
variant="light"
radius="full"
isIconOnly
onPress={closeSearch}
size="small"
type="text"
onClick={closeSearch}
aria-label={t('common.close')}
className="text-default-500 hover:text-default-900">
<X size={16} />
</Button>
icon={<X size={16} className="w-6" />}
className="text-default-500 hover:text-default-900"
/>
</div>
)
}

View File

@ -1,10 +1,10 @@
import { Avatar } from '@heroui/react'
import { getAgentTypeAvatar } from '@renderer/config/agent'
import type { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
import type { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
import { getAgentTypeLabel } from '@renderer/i18n/label'
import type { GetAgentResponse, GetAgentSessionResponse } from '@renderer/types'
import { isAgentEntity } from '@renderer/types'
import { Avatar } from 'antd'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
@ -42,7 +42,7 @@ const EssentialSettings: FC<EssentialSettingsProps> = ({ agentBase, update, show
<SettingsItem inline>
<SettingsTitle>{t('agent.type.label')}</SettingsTitle>
<div className="flex items-center gap-2">
<Avatar src={getAgentTypeAvatar(agentBase.type)} className="h-6 w-6 text-lg" />
<Avatar size={24} src={getAgentTypeAvatar(agentBase.type)} className="h-6 w-6 text-lg" />
<span>{(agentBase?.name ?? agentBase?.type) ? getAgentTypeLabel(agentBase.type) : ''}</span>
</div>
</SettingsItem>

View File

@ -1,7 +1,7 @@
import { cn } from '@heroui/react'
import EmojiIcon from '@renderer/components/EmojiIcon'
import { getAgentTypeLabel } from '@renderer/i18n/label'
import type { AgentEntity, AgentSessionEntity } from '@renderer/types'
import { cn } from '@renderer/utils'
import { Menu, Modal } from 'antd'
import type { ReactNode } from 'react'
import React from 'react'

View File

@ -3,7 +3,7 @@
import * as React from 'react'
import * as ContextMenuPrimitive from '@radix-ui/react-context-menu'
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
import { cn } from '@heroui/react'
import { cn } from '@renderer/utils'
function ContextMenu({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />

View File

@ -1,5 +1,7 @@
import type { HexColor } from '@renderer/types'
import { isHexColor } from '@renderer/types'
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
type ClassValue = string | number | boolean | undefined | null | ClassDictionary | ClassArray
@ -202,3 +204,7 @@ export function getForegroundColor(backgroundColor: HexColor): HexColor {
// `
// return acc
// }, {} as MediaQueries)
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}