refactor(selection): improve type safety and model handling for actions

- Replace FC type with direct props typing in ActionTranslate
- Add zod schema and type guards for builtin action items
- Enhance model selection logic in SelectionActionApp
This commit is contained in:
icarus 2025-11-25 17:56:40 +08:00
parent 63cde7c8ab
commit c2dde99947
3 changed files with 56 additions and 13 deletions

View File

@ -1,3 +1,5 @@
import * as z from 'zod'
import type { PreferenceSchemas } from './preferenceSchemas'
export type PreferenceDefaultScopeType = PreferenceSchemas['default']
@ -38,14 +40,38 @@ export type SelectionActionItem = {
searchEngine?: string
}
export type BuiltinActionItemId = 'translate' | 'explain' | 'summary' | 'search' | 'copy' | 'refine' | 'quote'
const SelectionBuiltinActionItemIdSchema = z.enum([
'translate',
'explain',
'summary',
'search',
'copy',
'refine',
'quote'
])
export type SelectionBuiltinActionItemId = z.infer<typeof SelectionBuiltinActionItemIdSchema>
export function isBuiltinActionItemId(id: string): id is SelectionBuiltinActionItemId {
return SelectionBuiltinActionItemIdSchema.safeParse(id).success
}
export interface SelectionBuiltinActionItem extends SelectionActionItem {
id: BuiltinActionItemId
id: SelectionBuiltinActionItemId
isBuiltIn: true
assistantId?: never
}
export function isSelectionBuiltinActionItem(
item: SelectionActionItem | null | undefined
): item is SelectionBuiltinActionItem {
if (!item) {
return false
}
return isBuiltinActionItemId(item.id)
}
export enum ThemeMode {
light = 'light',
dark = 'dark',

View File

@ -2,9 +2,11 @@ import { Button, Tooltip } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference'
import { isMac } from '@renderer/config/constant'
import i18n from '@renderer/i18n'
import { getAssistantById } from '@renderer/services/AssistantService'
import { getAssistantById, getDefaultModel, getTranslateModel } from '@renderer/services/AssistantService'
import { getProviderNameById } from '@renderer/services/ProviderService'
import type { Model } from '@renderer/types'
import { defaultLanguage } from '@shared/config/constant'
import type { SelectionActionItem } from '@shared/data/preference/preferenceTypes'
import { isSelectionBuiltinActionItem, type SelectionActionItem } from '@shared/data/preference/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { Slider } from 'antd'
import { Droplet, Minus, Pin, X } from 'lucide-react'
@ -23,13 +25,25 @@ const SelectionActionApp: FC = () => {
const { t } = useTranslation()
const [action, setAction] = useState<SelectionActionItem | null>(null)
const assistant = useMemo(() => {
if (action?.assistantId) {
return getAssistantById(action.assistantId)
} else {
return null
const model: Model | undefined = useMemo(() => {
if (isSelectionBuiltinActionItem(action)) {
switch (action.id) {
case 'translate':
return getTranslateModel()
case 'explain':
case 'summary':
case 'refine':
return getDefaultModel()
default:
return undefined
}
}
}, [action?.assistantId])
if (action?.assistantId) {
const assistant = getAssistantById(action.assistantId)
return assistant?.model
}
return undefined
}, [action])
const isActionLoaded = useRef(false)
@ -214,7 +228,11 @@ const SelectionActionApp: FC = () => {
</TitleBarIcon>
)}
<TitleBarCaption>{action.isBuiltIn ? t(action.name) : action.name}</TitleBarCaption>
{assistant?.model !== undefined && <span className="text-muted-foreground">{assistant.model.name}</span>}
{model !== undefined && (
<span className="text-muted-foreground">
{getProviderNameById(model.provider)} | {model.name}
</span>
)}
<TitleBarButtons>
<Tooltip
content={isPinned ? t('selection.action.window.pinned') : t('selection.action.window.pin')}

View File

@ -17,7 +17,6 @@ import { detectLanguage } from '@renderer/utils/translate'
import { defaultLanguage } from '@shared/config/constant'
import type { SelectionActionItem } from '@shared/data/preference/preferenceTypes'
import { ArrowRightFromLine, ArrowRightToLine, ChevronDown, CircleHelp, Globe } from 'lucide-react'
import type { FC } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -31,7 +30,7 @@ interface Props {
const logger = loggerService.withContext('ActionTranslate')
const ActionTranslate: FC<Props> = ({ action, scrollToBottom }) => {
const ActionTranslate = ({ action, scrollToBottom }: Props) => {
const { t } = useTranslation()
const [language] = usePreference('app.language')