Compare commits

...

4 Commits

Author SHA1 Message Date
icarus
f9f1a723da style(selection): truncate provider and model name in action app to prevent overflow 2025-11-25 18:01:28 +08:00
icarus
c2dde99947 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
2025-11-25 17:56:40 +08:00
icarus
63cde7c8ab feat(selection): display assistant model name in action window
Add assistant model name display when available to provide more context about the selected action
2025-11-25 17:36:41 +08:00
icarus
913238c991 feat(selection): add builtin action item type and update default items
Add new SelectionBuiltinActionItem type to better type builtin actions
Update defaultActionItems to use new type with const assertion
2025-11-25 17:36:29 +08:00
4 changed files with 68 additions and 7 deletions

View File

@@ -1,3 +1,5 @@
import * as z from 'zod'
import type { PreferenceSchemas } from './preferenceSchemas'
export type PreferenceDefaultScopeType = PreferenceSchemas['default']
@@ -38,6 +40,38 @@ export type SelectionActionItem = {
searchEngine?: string
}
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: 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

@@ -3,7 +3,7 @@
*/
import type { PayloadAction } from '@reduxjs/toolkit'
import { createSlice } from '@reduxjs/toolkit'
import type { SelectionActionItem } from '@shared/data/preference/preferenceTypes'
import type { SelectionActionItem, SelectionBuiltinActionItem } from '@shared/data/preference/preferenceTypes'
import { SelectionFilterMode, SelectionTriggerMode } from '@shared/data/preference/preferenceTypes'
export interface SelectionState {
@@ -20,7 +20,7 @@ export interface SelectionState {
actionItems: SelectionActionItem[]
}
export const defaultActionItems: SelectionActionItem[] = [
export const defaultActionItems = [
{ id: 'translate', name: 'selection.action.builtin.translate', enabled: true, isBuiltIn: true, icon: 'languages' },
{ id: 'explain', name: 'selection.action.builtin.explain', enabled: true, isBuiltIn: true, icon: 'file-question' },
{ id: 'summary', name: 'selection.action.builtin.summary', enabled: true, isBuiltIn: true, icon: 'scan-text' },
@@ -35,7 +35,7 @@ export const defaultActionItems: SelectionActionItem[] = [
{ id: 'copy', name: 'selection.action.builtin.copy', enabled: true, isBuiltIn: true, icon: 'clipboard-copy' },
{ id: 'refine', name: 'selection.action.builtin.refine', enabled: false, isBuiltIn: true, icon: 'wand-sparkles' },
{ id: 'quote', name: 'selection.action.builtin.quote', enabled: false, isBuiltIn: true, icon: 'quote' }
]
] as const satisfies SelectionBuiltinActionItem[]
export const initialState: SelectionState = {
selectionEnabled: false,

View File

@@ -2,14 +2,17 @@ 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, 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'
import { DynamicIcon } from 'lucide-react/dynamic'
import type { FC } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@@ -22,6 +25,26 @@ const SelectionActionApp: FC = () => {
const { t } = useTranslation()
const [action, setAction] = useState<SelectionActionItem | null>(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
}
}
if (action?.assistantId) {
const assistant = getAssistantById(action.assistantId)
return assistant?.model
}
return undefined
}, [action])
const isActionLoaded = useRef(false)
const [isAutoClose] = usePreference('feature.selection.auto_close')
@@ -205,6 +228,11 @@ const SelectionActionApp: FC = () => {
</TitleBarIcon>
)}
<TitleBarCaption>{action.isBuiltIn ? t(action.name) : action.name}</TitleBarCaption>
{model !== undefined && (
<span className="truncate 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')