diff --git a/eslint.config.mjs b/eslint.config.mjs index 5bad77bb7..668e35bfe 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -69,29 +69,9 @@ export default defineConfig([ ...oxlint.configs['flat/eslint'], ...oxlint.configs['flat/typescript'], ...oxlint.configs['flat/unicorn'], + // Custom rules should be after oxlint to overwrite + // LoggerService Custom Rules - only apply to src directory { - ignores: [ - 'node_modules/**', - 'build/**', - 'dist/**', - 'out/**', - 'local/**', - '.yarn/**', - '.gitignore', - 'scripts/cloudflare-worker.js', - 'src/main/integration/nutstore/sso/lib/**', - 'src/main/integration/cherryin/index.js', - 'src/main/integration/nutstore/sso/lib/**', - 'src/renderer/src/ui/**', - 'packages/**/dist' - ] - }, - // turn off oxlint supported rules. - ...oxlint.configs['flat/eslint'], - ...oxlint.configs['flat/typescript'], - ...oxlint.configs['flat/unicorn'], - { - // LoggerService Custom Rules - only apply to src directory files: ['src/**/*.{ts,tsx,js,jsx}'], ignores: ['src/**/__tests__/**', 'src/**/__mocks__/**', 'src/**/*.test.*', 'src/preload/**'], rules: { @@ -105,6 +85,7 @@ export default defineConfig([ ] } }, + // i18n { files: ['**/*.{ts,tsx,js,jsx}'], languageOptions: { @@ -152,9 +133,11 @@ export default defineConfig([ 'i18n/no-template-in-t': 'warn' } }, + // ui migration { // Component Rules - prevent importing antd components when migration completed - files: ['src/**/*.{ts,tsx,js,jsx}'], + files: ['**/*.{ts,tsx,js,jsx}'], + ignores: ['src/renderer/src/windows/dataRefactorTest/**/*.{ts,tsx}'], rules: { 'no-restricted-imports': [ 'error', @@ -162,8 +145,7 @@ export default defineConfig([ paths: [ { name: 'antd', - // TODO: migrate message again - importNames: ['Flex', 'Switch'], + importNames: ['Flex', 'Switch', 'message',], message: '❌ Do not import this component from antd. Use our custom components instead: import { ... } from "@cherrystudio/ui"' }, @@ -176,5 +158,5 @@ export default defineConfig([ } ] } - } + }, ]) diff --git a/src/renderer/src/components/TopView/toast.ts b/packages/ui/src/components/base/Toast/index.ts similarity index 71% rename from src/renderer/src/components/TopView/toast.ts rename to packages/ui/src/components/base/Toast/index.ts index d1e572631..13b70088f 100644 --- a/src/renderer/src/components/TopView/toast.ts +++ b/packages/ui/src/components/base/Toast/index.ts @@ -1,5 +1,5 @@ import { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast' -import type { RequireSome } from '@renderer/types' +import type { RequireSome } from '@types' type AddToastProps = Parameters[0] type ToastPropsColored = Omit @@ -21,35 +21,35 @@ const createToast = (color: 'danger' | 'success' | 'warning' | 'default') => { * @param arg - Toast content (string) or toast options object * @returns Toast ID or null */ -export const error = createToast('danger') +const error = createToast('danger') /** * Display a success toast notification with green color * @param arg - Toast content (string) or toast options object * @returns Toast ID or null */ -export const success = createToast('success') +const success = createToast('success') /** * Display a warning toast notification with yellow color * @param arg - Toast content (string) or toast options object * @returns Toast ID or null */ -export const warning = createToast('warning') +const warning = createToast('warning') /** * Display an info toast notification with default color * @param arg - Toast content (string) or toast options object * @returns Toast ID or null */ -export const info = createToast('default') +const info = createToast('default') /** * Display a loading toast notification that resolves with a promise * @param args - Toast options object containing a promise to resolve * @returns Toast ID or null */ -export const loading = (args: RequireSome) => { +const loading = (args: RequireSome) => { // Disappear immediately by default if (args.timeout === undefined) { args.timeout = 1 @@ -57,7 +57,20 @@ export const loading = (args: RequireSome) => { return addToast(args) } -export const getToastUtilities = () => +export type ToastUtilities = { + getToastQueue: typeof getToastQueue + addToast: typeof addToast + closeToast: typeof closeToast + closeAll: typeof closeAll + isToastClosing: typeof isToastClosing + error: typeof error + success: typeof success + warning: typeof warning + info: typeof info + loading: typeof loading +} + +export const getToastUtilities = (): ToastUtilities => ({ getToastQueue, addToast, diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 8e83e2ea8..4c6f97649 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -12,6 +12,7 @@ export type { StatusTagProps, StatusType } from './base/StatusTag' export { ErrorTag, InfoTag, StatusTag, SuccessTag, WarnTag } from './base/StatusTag' export { Switch } from './base/Switch' export { default as TextBadge } from './base/TextBadge' +export { getToastUtilities, type ToastUtilities } from './base/Toast' // Display Components export { default as Ellipsis } from './display/Ellipsis' diff --git a/packages/ui/src/types/index.ts b/packages/ui/src/types/index.ts index e69de29bb..6b44ed985 100644 --- a/packages/ui/src/types/index.ts +++ b/packages/ui/src/types/index.ts @@ -0,0 +1,15 @@ +/** + * Makes specified properties required while keeping others as is + * @template T - The object type to modify + * @template K - Keys of T that should be required + * @example + * type User = { + * name?: string; + * age?: number; + * } + * + * type UserWithName = RequireSome + * // Result: { name: string; age?: number; } + */ +// The type is copied from src/renderer/src/types/index.ts. +export type RequireSome = Omit & Required> diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index b41decfb9..7ab18cbcc 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -12,6 +12,10 @@ "moduleResolution": "bundler", "noFallthroughCasesInSwitch": true, "outDir": "./dist", + "paths": { + "@types": ["src/types"], + "@utils": ["src/utils"] + }, "resolveJsonModule": true, "rootDir": ".", "skipLibCheck": true, diff --git a/src/renderer/src/components/LocalBackupManager.tsx b/src/renderer/src/components/LocalBackupManager.tsx index fdc64222d..13dbcf329 100644 --- a/src/renderer/src/components/LocalBackupManager.tsx +++ b/src/renderer/src/components/LocalBackupManager.tsx @@ -1,7 +1,7 @@ import { DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined } from '@ant-design/icons' import { restoreFromLocal } from '@renderer/services/BackupService' import { formatFileSize } from '@renderer/utils' -import { Button, message, Modal, Table, Tooltip } from 'antd' +import { Button, Modal, Table, Tooltip } from 'antd' import dayjs from 'dayjs' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -68,7 +68,7 @@ export function LocalBackupManager({ visible, onClose, localBackupDir, restoreMe const handleDeleteSelected = async () => { if (selectedRowKeys.length === 0) { - message.warning(t('settings.data.local.backup.manager.select.files.delete')) + window.toast.warning(t('settings.data.local.backup.manager.select.files.delete')) return } @@ -120,7 +120,7 @@ export function LocalBackupManager({ visible, onClose, localBackupDir, restoreMe setDeleting(true) try { await window.api.backup.deleteLocalBackupFile(fileName, localBackupDir) - message.success(t('settings.data.local.backup.manager.delete.success.single')) + window.toast.success(t('settings.data.local.backup.manager.delete.success.single')) await fetchBackupFiles() } catch (error: any) { window.toast.error(`${t('settings.data.local.backup.manager.delete.error')}: ${error.message}`) @@ -147,7 +147,7 @@ export function LocalBackupManager({ visible, onClose, localBackupDir, restoreMe setRestoring(true) try { await (restoreMethod || restoreFromLocal)(fileName) - message.success(t('settings.data.local.backup.manager.restore.success')) + window.toast.success(t('settings.data.local.backup.manager.restore.success')) onClose() // Close the modal } catch (error: any) { window.toast.error(`${t('settings.data.local.backup.manager.restore.error')}: ${error.message}`) diff --git a/src/renderer/src/components/RichEditor/components/ImageUploader.tsx b/src/renderer/src/components/RichEditor/components/ImageUploader.tsx index e74607d73..631fc6bd1 100644 --- a/src/renderer/src/components/RichEditor/components/ImageUploader.tsx +++ b/src/renderer/src/components/RichEditor/components/ImageUploader.tsx @@ -1,6 +1,6 @@ import { InboxOutlined, LinkOutlined, LoadingOutlined, UploadOutlined } from '@ant-design/icons' import { Flex } from '@cherrystudio/ui' -import { Button, Input, message, Modal, Spin, Tabs, Upload } from 'antd' +import { Button, Input, Modal, Spin, Tabs, Upload } from 'antd' const { Dragger } = Upload import type { RcFile } from 'antd/es/upload' @@ -71,24 +71,24 @@ export const ImageUploader: React.FC = ({ onImageSelect, vis // Validate file type const isImage = file.type.startsWith('image/') if (!isImage) { - message.error(t('richEditor.imageUploader.invalidType')) + window.toast.error(t('richEditor.imageUploader.invalidType')) return false } // Validate file size (max 10MB) const isLt10M = file.size / 1024 / 1024 < 10 if (!isLt10M) { - message.error(t('richEditor.imageUploader.tooLarge')) + window.toast.error(t('richEditor.imageUploader.tooLarge')) return false } // Convert to base64 and call callback const base64Url = await convertFileToBase64(file) onImageSelect(base64Url) - message.success(t('richEditor.imageUploader.uploadSuccess')) + window.toast.success(t('richEditor.imageUploader.uploadSuccess')) onClose() } catch (error) { - message.error(t('richEditor.imageUploader.uploadError')) + window.toast.error(t('richEditor.imageUploader.uploadError')) } finally { setLoading(false) } @@ -98,7 +98,7 @@ export const ImageUploader: React.FC = ({ onImageSelect, vis const handleUrlSubmit = () => { if (!urlInput.trim()) { - message.error(t('richEditor.imageUploader.urlRequired')) + window.toast.error(t('richEditor.imageUploader.urlRequired')) return } @@ -106,11 +106,11 @@ export const ImageUploader: React.FC = ({ onImageSelect, vis try { new URL(urlInput.trim()) onImageSelect(urlInput.trim()) - message.success(t('richEditor.imageUploader.embedSuccess')) + window.toast.success(t('richEditor.imageUploader.embedSuccess')) setUrlInput('') onClose() } catch { - message.error(t('richEditor.imageUploader.invalidUrl')) + window.toast.error(t('richEditor.imageUploader.invalidUrl')) } } diff --git a/src/renderer/src/components/TopView/index.tsx b/src/renderer/src/components/TopView/index.tsx index 6823b6b9a..c2869a391 100644 --- a/src/renderer/src/components/TopView/index.tsx +++ b/src/renderer/src/components/TopView/index.tsx @@ -1,5 +1,6 @@ // import { loggerService } from '@logger' import { Box } from '@cherrystudio/ui' +import { getToastUtilities } from '@cherrystudio/ui' import TopViewMinappContainer from '@renderer/components/MinApp/TopViewMinappContainer' import { useAppInit } from '@renderer/hooks/useAppInit' import { useShortcuts } from '@renderer/hooks/useShortcuts' @@ -7,8 +8,6 @@ import { Modal } from 'antd' import type { PropsWithChildren } from 'react' import React, { useCallback, useEffect, useRef, useState } from 'react' -import { getToastUtilities } from './toast' - let onPop = () => {} let onShow = ({ element, id }: { element: React.FC | React.ReactNode; id: string }) => { element diff --git a/src/renderer/src/components/WebdavBackupManager.tsx b/src/renderer/src/components/WebdavBackupManager.tsx index 6683cacfe..a77987141 100644 --- a/src/renderer/src/components/WebdavBackupManager.tsx +++ b/src/renderer/src/components/WebdavBackupManager.tsx @@ -1,7 +1,7 @@ import { DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined } from '@ant-design/icons' import { restoreFromWebdav } from '@renderer/services/BackupService' import { formatFileSize } from '@renderer/utils' -import { Button, message, Modal, Table, Tooltip } from 'antd' +import { Button, Modal, Table, Tooltip } from 'antd' import dayjs from 'dayjs' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -101,7 +101,7 @@ export function WebdavBackupManager({ const handleDeleteSelected = async () => { if (selectedRowKeys.length === 0) { - message.warning(t('settings.data.webdav.backup.manager.select.files.delete')) + window.toast.warning(t('settings.data.webdav.backup.manager.select.files.delete')) return } diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts index 653d2710e..eefc880a5 100644 --- a/src/renderer/src/env.d.ts +++ b/src/renderer/src/env.d.ts @@ -1,12 +1,10 @@ /// -import type { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast' +import type { ToastUtilities } from '@cherrystudio/ui' import type KeyvStorage from '@kangfenmao/keyv-storage' import type { HookAPI } from 'antd/es/modal/useModal' import type { NavigateFunction } from 'react-router-dom' -import type { error, info, loading, success, warning } from './components/TopView/toast' - interface ImportMetaEnv { VITE_RENDERER_INTEGRATED_MODEL: string } @@ -22,17 +20,6 @@ declare global { keyv: KeyvStorage store: any navigate: NavigateFunction - toast: { - getToastQueue: typeof getToastQueue - addToast: typeof addToast - closeToast: typeof closeToast - closeAll: typeof closeAll - isToastClosing: typeof isToastClosing - error: typeof error - success: typeof success - warning: typeof warning - info: typeof info - loading: typeof loading - } + toast: ToastUtilities } } diff --git a/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx index f5a19904e..7124badd5 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx @@ -4,7 +4,7 @@ import { loggerService } from '@logger' import ThinkingEffect from '@renderer/components/ThinkingEffect' import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue' import { MessageBlockStatus, type ThinkingMessageBlock } from '@renderer/types/newMessage' -import { Collapse, message as antdMessage, Tooltip } from 'antd' +import { Collapse, Tooltip } from 'antd' import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -39,12 +39,12 @@ const ThinkingBlock: React.FC = ({ block }) => { navigator.clipboard .writeText(block.content) .then(() => { - antdMessage.success({ content: t('message.copied'), key: 'copy-message' }) + window.toast.success(t('message.copied')) setCopied(true) }) .catch((error) => { logger.error('Failed to copy text:', error) - antdMessage.error({ content: t('message.copy.failed'), key: 'copy-message-error' }) + window.toast.error(t('message.copy.failed')) }) } }, [block.content, setCopied, t]) diff --git a/src/renderer/src/pages/home/Messages/CitationsList.tsx b/src/renderer/src/pages/home/Messages/CitationsList.tsx index c9967d9f6..0e9ca2265 100644 --- a/src/renderer/src/pages/home/Messages/CitationsList.tsx +++ b/src/renderer/src/pages/home/Messages/CitationsList.tsx @@ -6,7 +6,7 @@ import type { Citation } from '@renderer/types' import { fetchWebContent } from '@renderer/utils/fetch' import { cleanMarkdownContent } from '@renderer/utils/formats' import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query' -import { Button, message, Popover, Skeleton } from 'antd' +import { Button, Popover, Skeleton } from 'antd' import { Check, Copy, FileSearch } from 'lucide-react' import React from 'react' import { useTranslation } from 'react-i18next' @@ -129,7 +129,7 @@ const CopyButton: React.FC<{ content: string }> = ({ content }) => { window.toast.success(t('common.copied')) }) .catch(() => { - message.error(t('message.copy.failed')) + window.toast.error(t('message.copy.failed')) }) } diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx index 14822b93d..6893086c7 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx @@ -10,18 +10,7 @@ import { isToolAutoApproved } from '@renderer/utils/mcp-tools' import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation' import type { MCPProgressEvent } from '@shared/config/types' import { IpcChannel } from '@shared/IpcChannel' -import { - Button, - Collapse, - ConfigProvider, - Dropdown, - message as antdMessage, - Modal, - Progress, - Tabs, - Tooltip -} from 'antd' -import { message } from 'antd' +import { Button, Collapse, ConfigProvider, Dropdown, Modal, Progress, Tabs, Tooltip } from 'antd' import { Check, ChevronDown, @@ -153,7 +142,7 @@ const MessageMcpTool: FC = ({ block }) => { const copyContent = (content: string, toolId: string) => { navigator.clipboard.writeText(content) - antdMessage.success({ content: t('message.copied'), key: 'copy-message' }) + window.toast.success(t('message.copied')) setCopiedMap((prev) => ({ ...prev, [toolId]: true })) setTimeoutTimer('copyContent', () => setCopiedMap((prev) => ({ ...prev, [toolId]: false })), 2000) } @@ -180,11 +169,11 @@ const MessageMcpTool: FC = ({ block }) => { if (success) { window.toast.success(t('message.tools.aborted')) } else { - message.error({ content: t('message.tools.abort_failed'), key: 'abort-tool' }) + window.toast.error(t('message.tools.abort_failed')) } } catch (error) { logger.error('Failed to abort tool:', error as Error) - message.error({ content: t('message.tools.abort_failed'), key: 'abort-tool' }) + window.toast.error(t('message.tools.abort_failed')) } } } @@ -490,7 +479,7 @@ const MessageMcpTool: FC = ({ block }) => { ? expandedResponse.content : JSON.stringify(expandedResponse.content, null, 2) ) - antdMessage.success({ content: t('message.copied'), key: 'copy-expanded' }) + window.toast.success(t('message.copied')) }} aria-label={t('common.copy')}> diff --git a/src/renderer/src/pages/knowledge/components/KnowledgeSearchItem/hooks.ts b/src/renderer/src/pages/knowledge/components/KnowledgeSearchItem/hooks.ts index d5159363f..a6d2d9716 100644 --- a/src/renderer/src/pages/knowledge/components/KnowledgeSearchItem/hooks.ts +++ b/src/renderer/src/pages/knowledge/components/KnowledgeSearchItem/hooks.ts @@ -1,6 +1,5 @@ import { loggerService } from '@logger' import { isValidUrl } from '@renderer/utils/fetch' -import { message } from 'antd' import type { ReactElement } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' @@ -35,7 +34,7 @@ export const useCopyText = () => { const handleCopy = async (text: string) => { try { await navigator.clipboard.writeText(text) - message.success(t('message.copied')) + window.toast.success(t('message.copied')) } catch (error) { logger.error('Failed to copy text:', error as Error) window.toast.error(t('message.error.copy') || 'Failed to copy text') diff --git a/src/renderer/src/pages/knowledge/items/KnowledgeSitemaps.tsx b/src/renderer/src/pages/knowledge/items/KnowledgeSitemaps.tsx index 2f8d32d22..e81a1beb9 100644 --- a/src/renderer/src/pages/knowledge/items/KnowledgeSitemaps.tsx +++ b/src/renderer/src/pages/knowledge/items/KnowledgeSitemaps.tsx @@ -7,7 +7,7 @@ import { useKnowledge } from '@renderer/hooks/useKnowledge' import FileItem from '@renderer/pages/files/FileItem' import { getProviderName } from '@renderer/services/ProviderService' import type { KnowledgeBase, KnowledgeItem } from '@renderer/types' -import { Button, message, Tooltip } from 'antd' +import { Button, Tooltip } from 'antd' import dayjs from 'dayjs' import { PlusIcon } from 'lucide-react' import type { FC } from 'react' @@ -74,7 +74,7 @@ const KnowledgeSitemaps: FC = ({ selectedBase }) => { try { new URL(url) if (sitemapItems.find((item) => item.content === url)) { - message.success(t('knowledge.sitemap_added')) + window.toast.success(t('knowledge.sitemap_added')) return } addSitemap(url) diff --git a/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx b/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx index 7b3925d5b..d3ed1181e 100644 --- a/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx +++ b/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx @@ -4,7 +4,7 @@ import { usePreference } from '@data/hooks/usePreference' import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { useMinapps } from '@renderer/hooks/useMinapps' import { SettingDescription, SettingDivider, SettingRowTitle, SettingTitle } from '@renderer/pages/settings' -import { Button, message, Slider, Tooltip } from 'antd' +import { Button, Slider, Tooltip } from 'antd' import type { FC } from 'react' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -28,7 +28,6 @@ const MiniAppSettings: FC = () => { const [visibleMiniApps, setVisibleMiniApps] = useState(minapps) const [disabledMiniApps, setDisabledMiniApps] = useState(disabled || []) - const [messageApi, contextHolder] = message.useMessage() const debounceTimerRef = useRef(null) const handleResetMinApps = useCallback(() => { @@ -47,8 +46,8 @@ const MiniAppSettings: FC = () => { // 恢复默认缓存数量 const handleResetCacheLimit = useCallback(() => { setMaxKeepAliveMinapps(DEFAULT_MAX_KEEPALIVE) - messageApi.info(t('settings.miniapps.cache_change_notice')) - }, [messageApi, t, setMaxKeepAliveMinapps]) + window.toast.info(t('settings.miniapps.cache_change_notice')) + }, [t, setMaxKeepAliveMinapps]) // 处理缓存数量变更 const handleCacheChange = useCallback( @@ -60,11 +59,11 @@ const MiniAppSettings: FC = () => { } debounceTimerRef.current = setTimeout(() => { - messageApi.info(t('settings.miniapps.cache_change_notice')) + window.toast.info(t('settings.miniapps.cache_change_notice')) debounceTimerRef.current = null }, 500) }, - [messageApi, t, setMaxKeepAliveMinapps] + [t, setMaxKeepAliveMinapps] ) // 组件卸载时清除定时器 @@ -78,7 +77,6 @@ const MiniAppSettings: FC = () => { return ( - {contextHolder} {/* 添加消息上下文 */} diff --git a/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx b/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx index f5fc045d3..f1be29193 100644 --- a/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx +++ b/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx @@ -3,7 +3,6 @@ import type { DraggableProvided, DroppableProvided, DropResult } from '@hello-pa import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd' import { getSidebarIconLabel } from '@renderer/i18n/label' import type { SidebarIcon } from '@shared/data/preference/preferenceTypes' -import { message } from 'antd' import { Code, FileSearch, @@ -44,7 +43,7 @@ const SidebarIconsManager: FC = ({ // 如果是chat图标且目标是disabled区域,则不允许移动并提示 const draggedItem = source.droppableId === 'visible' ? visibleIcons[source.index] : invisibleIcons[source.index] if (draggedItem === 'assistants' && destination.droppableId === 'disabled') { - message.warning(t('settings.display.sidebar.chat.hiddenMessage')) + window.toast.warning(t('settings.display.sidebar.chat.hiddenMessage')) return } @@ -81,7 +80,7 @@ const SidebarIconsManager: FC = ({ (icon: SidebarIcon, fromList: 'visible' | 'disabled') => { // 如果是chat图标且要移动到disabled列表,则不允许并提示 if (icon === 'assistants' && fromList === 'visible') { - message.warning(t('settings.display.sidebar.chat.hiddenMessage')) + window.toast.warning(t('settings.display.sidebar.chat.hiddenMessage')) return } diff --git a/src/renderer/src/pages/settings/NotesSettings.tsx b/src/renderer/src/pages/settings/NotesSettings.tsx index 01b375f5d..09b181008 100644 --- a/src/renderer/src/pages/settings/NotesSettings.tsx +++ b/src/renderer/src/pages/settings/NotesSettings.tsx @@ -5,7 +5,7 @@ import { useTheme } from '@renderer/context/ThemeProvider' import { useNotesSettings } from '@renderer/hooks/useNotesSettings' import { initWorkSpace } from '@renderer/services/NotesService' import type { EditorView } from '@renderer/types' -import { Button, Input, message, Slider } from 'antd' +import { Button, Input, Slider } from 'antd' import { FolderOpen } from 'lucide-react' import type { FC } from 'react' import { useEffect, useState } from 'react' @@ -50,7 +50,7 @@ const NotesSettings: FC = () => { } } catch (error) { logger.error('Failed to select directory:', error as Error) - message.error(t('notes.settings.data.select_directory_failed')) + window.toast.error(t('notes.settings.data.select_directory_failed')) } finally { setIsSelecting(false) } @@ -58,7 +58,7 @@ const NotesSettings: FC = () => { const handleApplyPath = async () => { if (!tempPath) { - message.error(t('notes.settings.data.path_required')) + window.toast.error(t('notes.settings.data.path_required')) return } @@ -67,7 +67,7 @@ const NotesSettings: FC = () => { const isValidDir = await window.api.file.validateNotesDirectory(tempPath) if (!isValidDir) { - message.error(t('notes.settings.data.invalid_directory')) + window.toast.error(t('notes.settings.data.invalid_directory')) return } diff --git a/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx b/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx index 0c40aa3e1..6eb632f97 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx @@ -23,7 +23,7 @@ import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth' import type { Model, ModelCapability, ModelType, Provider } from '@renderer/types' import { getDefaultGroupName, getDifference, getUnion, uniqueObjectArray } from '@renderer/utils' import type { ModalProps } from 'antd' -import { Button, Divider, Form, Input, InputNumber, message, Modal, Select, Tooltip } from 'antd' +import { Button, Divider, Form, Input, InputNumber, Modal, Select, Tooltip } from 'antd' import { cloneDeep } from 'lodash' import { ChevronDown, ChevronUp, RotateCcw, SaveIcon } from 'lucide-react' import type { FC } from 'react' @@ -281,7 +281,7 @@ const ModelEditContent: FC = ({ provider, mo onClick={() => { const val = form.getFieldValue('name') navigator.clipboard.writeText((val.id || model.id) as string) - message.success(t('message.copied')) + window.toast.success(t('message.copied')) }} /> } diff --git a/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx b/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx index 207d1fe10..0c91ef382 100644 --- a/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx +++ b/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx @@ -1,3 +1,4 @@ +import { getToastUtilities } from '@cherrystudio/ui' import { AppLogo } from '@renderer/config/env' import { loggerService } from '@renderer/services/LoggerService' import { IpcChannel } from '@shared/IpcChannel' @@ -33,6 +34,10 @@ const MigrateApp: React.FC = () => { message: 'Ready to start data migration' }) + useEffect(() => { + window.toast = getToastUtilities() + }, []) + useEffect(() => { // Listen for progress updates const handleProgress = (_: any, progressData: MigrationProgress) => { diff --git a/src/renderer/src/windows/dataRefactorTest/TestApp.tsx b/src/renderer/src/windows/dataRefactorTest/TestApp.tsx index f083c7737..47a84916c 100644 --- a/src/renderer/src/windows/dataRefactorTest/TestApp.tsx +++ b/src/renderer/src/windows/dataRefactorTest/TestApp.tsx @@ -1,10 +1,11 @@ +import { getToastUtilities } from '@cherrystudio/ui' import { AppLogo } from '@renderer/config/env' import { usePreference } from '@renderer/data/hooks/usePreference' import { loggerService } from '@renderer/services/LoggerService' import { ThemeMode } from '@shared/data/preference/preferenceTypes' import { Button, Card, Col, Divider, Layout, Row, Space, Tabs, Typography } from 'antd' import { Activity, AlertTriangle, Database, FlaskConical, Settings, TestTube, TrendingUp, Zap } from 'lucide-react' -import React from 'react' +import React, { useEffect } from 'react' import styled from 'styled-components' import CacheAdvancedTests from './components/CacheAdvancedTests' @@ -54,6 +55,10 @@ const TestApp: React.FC = () => { return Math.floor(Date.now() / 1000) % 100 } + useEffect(() => { + window.toast = getToastUtilities() + }, []) + const windowNumber = getWindowNumber() // Add theme preference monitoring for UI changes diff --git a/src/renderer/src/windows/mini/MiniWindowApp.tsx b/src/renderer/src/windows/mini/MiniWindowApp.tsx index da9bad816..5a2f44ec5 100644 --- a/src/renderer/src/windows/mini/MiniWindowApp.tsx +++ b/src/renderer/src/windows/mini/MiniWindowApp.tsx @@ -1,9 +1,9 @@ import '@renderer/databases' +import { getToastUtilities } from '@cherrystudio/ui' import { usePreference } from '@data/hooks/usePreference' import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { ToastPortal } from '@renderer/components/ToastPortal' -import { getToastUtilities } from '@renderer/components/TopView/toast' import { HeroUIProvider } from '@renderer/context/HeroUIProvider' import store, { persistor } from '@renderer/store' import { useEffect } from 'react' diff --git a/src/renderer/src/windows/selection/action/entryPoint.tsx b/src/renderer/src/windows/selection/action/entryPoint.tsx index 39e15bfe8..82cac5a5b 100644 --- a/src/renderer/src/windows/selection/action/entryPoint.tsx +++ b/src/renderer/src/windows/selection/action/entryPoint.tsx @@ -2,11 +2,11 @@ import '@renderer/assets/styles/index.css' import '@renderer/assets/styles/tailwind.css' import '@ant-design/v5-patch-for-react-19' +import { getToastUtilities } from '@cherrystudio/ui' import { preferenceService } from '@data/PreferenceService' import KeyvStorage from '@kangfenmao/keyv-storage' import { loggerService } from '@logger' import { ToastPortal } from '@renderer/components/ToastPortal' -import { getToastUtilities } from '@renderer/components/TopView/toast' import AntdProvider from '@renderer/context/AntdProvider' import { CodeStyleProvider } from '@renderer/context/CodeStyleProvider' import { HeroUIProvider } from '@renderer/context/HeroUIProvider'