mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-10 23:59:45 +08:00
refactor: migrate to toast from antd message (#10233)
* style(eslint): reorganize eslint config comments and rules Move comments to consistent positions above their corresponding rules Update antd import restriction to include 'message' component * fix(eslint): reorganize eslint config to enable custom rules * fix(eslint): update antd import restriction to include message Prevent direct imports of both Flex and message from antd, enforcing usage of custom components * feat(migration): add toast utilities to migrate and test apps Initialize toast utilities on window object for both migration and test applications to enable toast notifications * build(ui): add path aliases for types and utils modules * refactor(toast): move toast utilities to ui package for better reusability Centralize toast utilities in the @cherrystudio/ui package to improve code organization and reuse across multiple components. This change includes: - Moving toast implementation to ui package - Updating all imports to use the new location - Adding proper type definitions * refactor: replace antd message with window.toast for consistency Replace all instances of antd's message component with window.toast throughout the application to maintain consistent notification behavior. Also add an ignore rule for dataRefactorTest files in eslint config.
This commit is contained in:
parent
d4fd8ffdcc
commit
5ac09d5311
@ -69,29 +69,9 @@ export default defineConfig([
|
|||||||
...oxlint.configs['flat/eslint'],
|
...oxlint.configs['flat/eslint'],
|
||||||
...oxlint.configs['flat/typescript'],
|
...oxlint.configs['flat/typescript'],
|
||||||
...oxlint.configs['flat/unicorn'],
|
...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}'],
|
files: ['src/**/*.{ts,tsx,js,jsx}'],
|
||||||
ignores: ['src/**/__tests__/**', 'src/**/__mocks__/**', 'src/**/*.test.*', 'src/preload/**'],
|
ignores: ['src/**/__tests__/**', 'src/**/__mocks__/**', 'src/**/*.test.*', 'src/preload/**'],
|
||||||
rules: {
|
rules: {
|
||||||
@ -105,6 +85,7 @@ export default defineConfig([
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// i18n
|
||||||
{
|
{
|
||||||
files: ['**/*.{ts,tsx,js,jsx}'],
|
files: ['**/*.{ts,tsx,js,jsx}'],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
@ -152,9 +133,11 @@ export default defineConfig([
|
|||||||
'i18n/no-template-in-t': 'warn'
|
'i18n/no-template-in-t': 'warn'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// ui migration
|
||||||
{
|
{
|
||||||
// Component Rules - prevent importing antd components when migration completed
|
// 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: {
|
rules: {
|
||||||
'no-restricted-imports': [
|
'no-restricted-imports': [
|
||||||
'error',
|
'error',
|
||||||
@ -162,8 +145,7 @@ export default defineConfig([
|
|||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
name: 'antd',
|
name: 'antd',
|
||||||
// TODO: migrate message again
|
importNames: ['Flex', 'Switch', 'message',],
|
||||||
importNames: ['Flex', 'Switch'],
|
|
||||||
message:
|
message:
|
||||||
'❌ Do not import this component from antd. Use our custom components instead: import { ... } from "@cherrystudio/ui"'
|
'❌ Do not import this component from antd. Use our custom components instead: import { ... } from "@cherrystudio/ui"'
|
||||||
},
|
},
|
||||||
@ -176,5 +158,5 @@ export default defineConfig([
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast'
|
import { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast'
|
||||||
import type { RequireSome } from '@renderer/types'
|
import type { RequireSome } from '@types'
|
||||||
|
|
||||||
type AddToastProps = Parameters<typeof addToast>[0]
|
type AddToastProps = Parameters<typeof addToast>[0]
|
||||||
type ToastPropsColored = Omit<AddToastProps, 'color'>
|
type ToastPropsColored = Omit<AddToastProps, 'color'>
|
||||||
@ -21,35 +21,35 @@ const createToast = (color: 'danger' | 'success' | 'warning' | 'default') => {
|
|||||||
* @param arg - Toast content (string) or toast options object
|
* @param arg - Toast content (string) or toast options object
|
||||||
* @returns Toast ID or null
|
* @returns Toast ID or null
|
||||||
*/
|
*/
|
||||||
export const error = createToast('danger')
|
const error = createToast('danger')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a success toast notification with green color
|
* Display a success toast notification with green color
|
||||||
* @param arg - Toast content (string) or toast options object
|
* @param arg - Toast content (string) or toast options object
|
||||||
* @returns Toast ID or null
|
* @returns Toast ID or null
|
||||||
*/
|
*/
|
||||||
export const success = createToast('success')
|
const success = createToast('success')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a warning toast notification with yellow color
|
* Display a warning toast notification with yellow color
|
||||||
* @param arg - Toast content (string) or toast options object
|
* @param arg - Toast content (string) or toast options object
|
||||||
* @returns Toast ID or null
|
* @returns Toast ID or null
|
||||||
*/
|
*/
|
||||||
export const warning = createToast('warning')
|
const warning = createToast('warning')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display an info toast notification with default color
|
* Display an info toast notification with default color
|
||||||
* @param arg - Toast content (string) or toast options object
|
* @param arg - Toast content (string) or toast options object
|
||||||
* @returns Toast ID or null
|
* @returns Toast ID or null
|
||||||
*/
|
*/
|
||||||
export const info = createToast('default')
|
const info = createToast('default')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a loading toast notification that resolves with a promise
|
* Display a loading toast notification that resolves with a promise
|
||||||
* @param args - Toast options object containing a promise to resolve
|
* @param args - Toast options object containing a promise to resolve
|
||||||
* @returns Toast ID or null
|
* @returns Toast ID or null
|
||||||
*/
|
*/
|
||||||
export const loading = (args: RequireSome<AddToastProps, 'promise'>) => {
|
const loading = (args: RequireSome<AddToastProps, 'promise'>) => {
|
||||||
// Disappear immediately by default
|
// Disappear immediately by default
|
||||||
if (args.timeout === undefined) {
|
if (args.timeout === undefined) {
|
||||||
args.timeout = 1
|
args.timeout = 1
|
||||||
@ -57,7 +57,20 @@ export const loading = (args: RequireSome<AddToastProps, 'promise'>) => {
|
|||||||
return addToast(args)
|
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,
|
getToastQueue,
|
||||||
addToast,
|
addToast,
|
||||||
@ -12,6 +12,7 @@ export type { StatusTagProps, StatusType } from './base/StatusTag'
|
|||||||
export { ErrorTag, InfoTag, StatusTag, SuccessTag, WarnTag } from './base/StatusTag'
|
export { ErrorTag, InfoTag, StatusTag, SuccessTag, WarnTag } from './base/StatusTag'
|
||||||
export { Switch } from './base/Switch'
|
export { Switch } from './base/Switch'
|
||||||
export { default as TextBadge } from './base/TextBadge'
|
export { default as TextBadge } from './base/TextBadge'
|
||||||
|
export { getToastUtilities, type ToastUtilities } from './base/Toast'
|
||||||
|
|
||||||
// Display Components
|
// Display Components
|
||||||
export { default as Ellipsis } from './display/Ellipsis'
|
export { default as Ellipsis } from './display/Ellipsis'
|
||||||
|
|||||||
@ -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<User, 'name'>
|
||||||
|
* // Result: { name: string; age?: number; }
|
||||||
|
*/
|
||||||
|
// The type is copied from src/renderer/src/types/index.ts.
|
||||||
|
export type RequireSome<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
|
||||||
@ -12,6 +12,10 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
|
"paths": {
|
||||||
|
"@types": ["src/types"],
|
||||||
|
"@utils": ["src/utils"]
|
||||||
|
},
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined } from '@ant-design/icons'
|
import { DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined } from '@ant-design/icons'
|
||||||
import { restoreFromLocal } from '@renderer/services/BackupService'
|
import { restoreFromLocal } from '@renderer/services/BackupService'
|
||||||
import { formatFileSize } from '@renderer/utils'
|
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 dayjs from 'dayjs'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -68,7 +68,7 @@ export function LocalBackupManager({ visible, onClose, localBackupDir, restoreMe
|
|||||||
|
|
||||||
const handleDeleteSelected = async () => {
|
const handleDeleteSelected = async () => {
|
||||||
if (selectedRowKeys.length === 0) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ export function LocalBackupManager({ visible, onClose, localBackupDir, restoreMe
|
|||||||
setDeleting(true)
|
setDeleting(true)
|
||||||
try {
|
try {
|
||||||
await window.api.backup.deleteLocalBackupFile(fileName, localBackupDir)
|
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()
|
await fetchBackupFiles()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
window.toast.error(`${t('settings.data.local.backup.manager.delete.error')}: ${error.message}`)
|
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)
|
setRestoring(true)
|
||||||
try {
|
try {
|
||||||
await (restoreMethod || restoreFromLocal)(fileName)
|
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
|
onClose() // Close the modal
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
window.toast.error(`${t('settings.data.local.backup.manager.restore.error')}: ${error.message}`)
|
window.toast.error(`${t('settings.data.local.backup.manager.restore.error')}: ${error.message}`)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { InboxOutlined, LinkOutlined, LoadingOutlined, UploadOutlined } from '@ant-design/icons'
|
import { InboxOutlined, LinkOutlined, LoadingOutlined, UploadOutlined } from '@ant-design/icons'
|
||||||
import { Flex } from '@cherrystudio/ui'
|
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
|
const { Dragger } = Upload
|
||||||
import type { RcFile } from 'antd/es/upload'
|
import type { RcFile } from 'antd/es/upload'
|
||||||
@ -71,24 +71,24 @@ export const ImageUploader: React.FC<ImageUploaderProps> = ({ onImageSelect, vis
|
|||||||
// Validate file type
|
// Validate file type
|
||||||
const isImage = file.type.startsWith('image/')
|
const isImage = file.type.startsWith('image/')
|
||||||
if (!isImage) {
|
if (!isImage) {
|
||||||
message.error(t('richEditor.imageUploader.invalidType'))
|
window.toast.error(t('richEditor.imageUploader.invalidType'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate file size (max 10MB)
|
// Validate file size (max 10MB)
|
||||||
const isLt10M = file.size / 1024 / 1024 < 10
|
const isLt10M = file.size / 1024 / 1024 < 10
|
||||||
if (!isLt10M) {
|
if (!isLt10M) {
|
||||||
message.error(t('richEditor.imageUploader.tooLarge'))
|
window.toast.error(t('richEditor.imageUploader.tooLarge'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to base64 and call callback
|
// Convert to base64 and call callback
|
||||||
const base64Url = await convertFileToBase64(file)
|
const base64Url = await convertFileToBase64(file)
|
||||||
onImageSelect(base64Url)
|
onImageSelect(base64Url)
|
||||||
message.success(t('richEditor.imageUploader.uploadSuccess'))
|
window.toast.success(t('richEditor.imageUploader.uploadSuccess'))
|
||||||
onClose()
|
onClose()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(t('richEditor.imageUploader.uploadError'))
|
window.toast.error(t('richEditor.imageUploader.uploadError'))
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ export const ImageUploader: React.FC<ImageUploaderProps> = ({ onImageSelect, vis
|
|||||||
|
|
||||||
const handleUrlSubmit = () => {
|
const handleUrlSubmit = () => {
|
||||||
if (!urlInput.trim()) {
|
if (!urlInput.trim()) {
|
||||||
message.error(t('richEditor.imageUploader.urlRequired'))
|
window.toast.error(t('richEditor.imageUploader.urlRequired'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,11 +106,11 @@ export const ImageUploader: React.FC<ImageUploaderProps> = ({ onImageSelect, vis
|
|||||||
try {
|
try {
|
||||||
new URL(urlInput.trim())
|
new URL(urlInput.trim())
|
||||||
onImageSelect(urlInput.trim())
|
onImageSelect(urlInput.trim())
|
||||||
message.success(t('richEditor.imageUploader.embedSuccess'))
|
window.toast.success(t('richEditor.imageUploader.embedSuccess'))
|
||||||
setUrlInput('')
|
setUrlInput('')
|
||||||
onClose()
|
onClose()
|
||||||
} catch {
|
} catch {
|
||||||
message.error(t('richEditor.imageUploader.invalidUrl'))
|
window.toast.error(t('richEditor.imageUploader.invalidUrl'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// import { loggerService } from '@logger'
|
// import { loggerService } from '@logger'
|
||||||
import { Box } from '@cherrystudio/ui'
|
import { Box } from '@cherrystudio/ui'
|
||||||
|
import { getToastUtilities } from '@cherrystudio/ui'
|
||||||
import TopViewMinappContainer from '@renderer/components/MinApp/TopViewMinappContainer'
|
import TopViewMinappContainer from '@renderer/components/MinApp/TopViewMinappContainer'
|
||||||
import { useAppInit } from '@renderer/hooks/useAppInit'
|
import { useAppInit } from '@renderer/hooks/useAppInit'
|
||||||
import { useShortcuts } from '@renderer/hooks/useShortcuts'
|
import { useShortcuts } from '@renderer/hooks/useShortcuts'
|
||||||
@ -7,8 +8,6 @@ import { Modal } from 'antd'
|
|||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
import { getToastUtilities } from './toast'
|
|
||||||
|
|
||||||
let onPop = () => {}
|
let onPop = () => {}
|
||||||
let onShow = ({ element, id }: { element: React.FC | React.ReactNode; id: string }) => {
|
let onShow = ({ element, id }: { element: React.FC | React.ReactNode; id: string }) => {
|
||||||
element
|
element
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined } from '@ant-design/icons'
|
import { DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined } from '@ant-design/icons'
|
||||||
import { restoreFromWebdav } from '@renderer/services/BackupService'
|
import { restoreFromWebdav } from '@renderer/services/BackupService'
|
||||||
import { formatFileSize } from '@renderer/utils'
|
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 dayjs from 'dayjs'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -101,7 +101,7 @@ export function WebdavBackupManager({
|
|||||||
|
|
||||||
const handleDeleteSelected = async () => {
|
const handleDeleteSelected = async () => {
|
||||||
if (selectedRowKeys.length === 0) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/renderer/src/env.d.ts
vendored
17
src/renderer/src/env.d.ts
vendored
@ -1,12 +1,10 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
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 KeyvStorage from '@kangfenmao/keyv-storage'
|
||||||
import type { HookAPI } from 'antd/es/modal/useModal'
|
import type { HookAPI } from 'antd/es/modal/useModal'
|
||||||
import type { NavigateFunction } from 'react-router-dom'
|
import type { NavigateFunction } from 'react-router-dom'
|
||||||
|
|
||||||
import type { error, info, loading, success, warning } from './components/TopView/toast'
|
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
VITE_RENDERER_INTEGRATED_MODEL: string
|
VITE_RENDERER_INTEGRATED_MODEL: string
|
||||||
}
|
}
|
||||||
@ -22,17 +20,6 @@ declare global {
|
|||||||
keyv: KeyvStorage
|
keyv: KeyvStorage
|
||||||
store: any
|
store: any
|
||||||
navigate: NavigateFunction
|
navigate: NavigateFunction
|
||||||
toast: {
|
toast: 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { loggerService } from '@logger'
|
|||||||
import ThinkingEffect from '@renderer/components/ThinkingEffect'
|
import ThinkingEffect from '@renderer/components/ThinkingEffect'
|
||||||
import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue'
|
import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue'
|
||||||
import { MessageBlockStatus, type ThinkingMessageBlock } from '@renderer/types/newMessage'
|
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 { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -39,12 +39,12 @@ const ThinkingBlock: React.FC<Props> = ({ block }) => {
|
|||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
.writeText(block.content)
|
.writeText(block.content)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
antdMessage.success({ content: t('message.copied'), key: 'copy-message' })
|
window.toast.success(t('message.copied'))
|
||||||
setCopied(true)
|
setCopied(true)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logger.error('Failed to copy text:', 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])
|
}, [block.content, setCopied, t])
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import type { Citation } from '@renderer/types'
|
|||||||
import { fetchWebContent } from '@renderer/utils/fetch'
|
import { fetchWebContent } from '@renderer/utils/fetch'
|
||||||
import { cleanMarkdownContent } from '@renderer/utils/formats'
|
import { cleanMarkdownContent } from '@renderer/utils/formats'
|
||||||
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
|
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 { Check, Copy, FileSearch } from 'lucide-react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -129,7 +129,7 @@ const CopyButton: React.FC<{ content: string }> = ({ content }) => {
|
|||||||
window.toast.success(t('common.copied'))
|
window.toast.success(t('common.copied'))
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
message.error(t('message.copy.failed'))
|
window.toast.error(t('message.copy.failed'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,18 +10,7 @@ import { isToolAutoApproved } from '@renderer/utils/mcp-tools'
|
|||||||
import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation'
|
import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation'
|
||||||
import type { MCPProgressEvent } from '@shared/config/types'
|
import type { MCPProgressEvent } from '@shared/config/types'
|
||||||
import { IpcChannel } from '@shared/IpcChannel'
|
import { IpcChannel } from '@shared/IpcChannel'
|
||||||
import {
|
import { Button, Collapse, ConfigProvider, Dropdown, Modal, Progress, Tabs, Tooltip } from 'antd'
|
||||||
Button,
|
|
||||||
Collapse,
|
|
||||||
ConfigProvider,
|
|
||||||
Dropdown,
|
|
||||||
message as antdMessage,
|
|
||||||
Modal,
|
|
||||||
Progress,
|
|
||||||
Tabs,
|
|
||||||
Tooltip
|
|
||||||
} from 'antd'
|
|
||||||
import { message } from 'antd'
|
|
||||||
import {
|
import {
|
||||||
Check,
|
Check,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
@ -153,7 +142,7 @@ const MessageMcpTool: FC<Props> = ({ block }) => {
|
|||||||
|
|
||||||
const copyContent = (content: string, toolId: string) => {
|
const copyContent = (content: string, toolId: string) => {
|
||||||
navigator.clipboard.writeText(content)
|
navigator.clipboard.writeText(content)
|
||||||
antdMessage.success({ content: t('message.copied'), key: 'copy-message' })
|
window.toast.success(t('message.copied'))
|
||||||
setCopiedMap((prev) => ({ ...prev, [toolId]: true }))
|
setCopiedMap((prev) => ({ ...prev, [toolId]: true }))
|
||||||
setTimeoutTimer('copyContent', () => setCopiedMap((prev) => ({ ...prev, [toolId]: false })), 2000)
|
setTimeoutTimer('copyContent', () => setCopiedMap((prev) => ({ ...prev, [toolId]: false })), 2000)
|
||||||
}
|
}
|
||||||
@ -180,11 +169,11 @@ const MessageMcpTool: FC<Props> = ({ block }) => {
|
|||||||
if (success) {
|
if (success) {
|
||||||
window.toast.success(t('message.tools.aborted'))
|
window.toast.success(t('message.tools.aborted'))
|
||||||
} else {
|
} else {
|
||||||
message.error({ content: t('message.tools.abort_failed'), key: 'abort-tool' })
|
window.toast.error(t('message.tools.abort_failed'))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to abort tool:', error as 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<Props> = ({ block }) => {
|
|||||||
? expandedResponse.content
|
? expandedResponse.content
|
||||||
: JSON.stringify(expandedResponse.content, null, 2)
|
: 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')}>
|
aria-label={t('common.copy')}>
|
||||||
<i className="iconfont icon-copy"></i>
|
<i className="iconfont icon-copy"></i>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { isValidUrl } from '@renderer/utils/fetch'
|
import { isValidUrl } from '@renderer/utils/fetch'
|
||||||
import { message } from 'antd'
|
|
||||||
import type { ReactElement } from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -35,7 +34,7 @@ export const useCopyText = () => {
|
|||||||
const handleCopy = async (text: string) => {
|
const handleCopy = async (text: string) => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text)
|
await navigator.clipboard.writeText(text)
|
||||||
message.success(t('message.copied'))
|
window.toast.success(t('message.copied'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to copy text:', error as Error)
|
logger.error('Failed to copy text:', error as Error)
|
||||||
window.toast.error(t('message.error.copy') || 'Failed to copy text')
|
window.toast.error(t('message.error.copy') || 'Failed to copy text')
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { useKnowledge } from '@renderer/hooks/useKnowledge'
|
|||||||
import FileItem from '@renderer/pages/files/FileItem'
|
import FileItem from '@renderer/pages/files/FileItem'
|
||||||
import { getProviderName } from '@renderer/services/ProviderService'
|
import { getProviderName } from '@renderer/services/ProviderService'
|
||||||
import type { KnowledgeBase, KnowledgeItem } from '@renderer/types'
|
import type { KnowledgeBase, KnowledgeItem } from '@renderer/types'
|
||||||
import { Button, message, Tooltip } from 'antd'
|
import { Button, Tooltip } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { PlusIcon } from 'lucide-react'
|
import { PlusIcon } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
@ -74,7 +74,7 @@ const KnowledgeSitemaps: FC<KnowledgeContentProps> = ({ selectedBase }) => {
|
|||||||
try {
|
try {
|
||||||
new URL(url)
|
new URL(url)
|
||||||
if (sitemapItems.find((item) => item.content === url)) {
|
if (sitemapItems.find((item) => item.content === url)) {
|
||||||
message.success(t('knowledge.sitemap_added'))
|
window.toast.success(t('knowledge.sitemap_added'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addSitemap(url)
|
addSitemap(url)
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { usePreference } from '@data/hooks/usePreference'
|
|||||||
import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
|
import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
|
||||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||||
import { SettingDescription, SettingDivider, SettingRowTitle, SettingTitle } from '@renderer/pages/settings'
|
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 type { FC } from 'react'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -28,7 +28,6 @@ const MiniAppSettings: FC = () => {
|
|||||||
|
|
||||||
const [visibleMiniApps, setVisibleMiniApps] = useState(minapps)
|
const [visibleMiniApps, setVisibleMiniApps] = useState(minapps)
|
||||||
const [disabledMiniApps, setDisabledMiniApps] = useState(disabled || [])
|
const [disabledMiniApps, setDisabledMiniApps] = useState(disabled || [])
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
|
||||||
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null)
|
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
|
||||||
const handleResetMinApps = useCallback(() => {
|
const handleResetMinApps = useCallback(() => {
|
||||||
@ -47,8 +46,8 @@ const MiniAppSettings: FC = () => {
|
|||||||
// 恢复默认缓存数量
|
// 恢复默认缓存数量
|
||||||
const handleResetCacheLimit = useCallback(() => {
|
const handleResetCacheLimit = useCallback(() => {
|
||||||
setMaxKeepAliveMinapps(DEFAULT_MAX_KEEPALIVE)
|
setMaxKeepAliveMinapps(DEFAULT_MAX_KEEPALIVE)
|
||||||
messageApi.info(t('settings.miniapps.cache_change_notice'))
|
window.toast.info(t('settings.miniapps.cache_change_notice'))
|
||||||
}, [messageApi, t, setMaxKeepAliveMinapps])
|
}, [t, setMaxKeepAliveMinapps])
|
||||||
|
|
||||||
// 处理缓存数量变更
|
// 处理缓存数量变更
|
||||||
const handleCacheChange = useCallback(
|
const handleCacheChange = useCallback(
|
||||||
@ -60,11 +59,11 @@ const MiniAppSettings: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debounceTimerRef.current = setTimeout(() => {
|
debounceTimerRef.current = setTimeout(() => {
|
||||||
messageApi.info(t('settings.miniapps.cache_change_notice'))
|
window.toast.info(t('settings.miniapps.cache_change_notice'))
|
||||||
debounceTimerRef.current = null
|
debounceTimerRef.current = null
|
||||||
}, 500)
|
}, 500)
|
||||||
},
|
},
|
||||||
[messageApi, t, setMaxKeepAliveMinapps]
|
[t, setMaxKeepAliveMinapps]
|
||||||
)
|
)
|
||||||
|
|
||||||
// 组件卸载时清除定时器
|
// 组件卸载时清除定时器
|
||||||
@ -78,7 +77,6 @@ const MiniAppSettings: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{contextHolder} {/* 添加消息上下文 */}
|
|
||||||
<SettingTitle style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center' }}>
|
<SettingTitle style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center' }}>
|
||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
<Button onClick={handleSwapMinApps}>{t('common.swap')}</Button>
|
<Button onClick={handleSwapMinApps}>{t('common.swap')}</Button>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import type { DraggableProvided, DroppableProvided, DropResult } from '@hello-pa
|
|||||||
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
|
||||||
import { getSidebarIconLabel } from '@renderer/i18n/label'
|
import { getSidebarIconLabel } from '@renderer/i18n/label'
|
||||||
import type { SidebarIcon } from '@shared/data/preference/preferenceTypes'
|
import type { SidebarIcon } from '@shared/data/preference/preferenceTypes'
|
||||||
import { message } from 'antd'
|
|
||||||
import {
|
import {
|
||||||
Code,
|
Code,
|
||||||
FileSearch,
|
FileSearch,
|
||||||
@ -44,7 +43,7 @@ const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
|
|||||||
// 如果是chat图标且目标是disabled区域,则不允许移动并提示
|
// 如果是chat图标且目标是disabled区域,则不允许移动并提示
|
||||||
const draggedItem = source.droppableId === 'visible' ? visibleIcons[source.index] : invisibleIcons[source.index]
|
const draggedItem = source.droppableId === 'visible' ? visibleIcons[source.index] : invisibleIcons[source.index]
|
||||||
if (draggedItem === 'assistants' && destination.droppableId === 'disabled') {
|
if (draggedItem === 'assistants' && destination.droppableId === 'disabled') {
|
||||||
message.warning(t('settings.display.sidebar.chat.hiddenMessage'))
|
window.toast.warning(t('settings.display.sidebar.chat.hiddenMessage'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +80,7 @@ const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
|
|||||||
(icon: SidebarIcon, fromList: 'visible' | 'disabled') => {
|
(icon: SidebarIcon, fromList: 'visible' | 'disabled') => {
|
||||||
// 如果是chat图标且要移动到disabled列表,则不允许并提示
|
// 如果是chat图标且要移动到disabled列表,则不允许并提示
|
||||||
if (icon === 'assistants' && fromList === 'visible') {
|
if (icon === 'assistants' && fromList === 'visible') {
|
||||||
message.warning(t('settings.display.sidebar.chat.hiddenMessage'))
|
window.toast.warning(t('settings.display.sidebar.chat.hiddenMessage'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useTheme } from '@renderer/context/ThemeProvider'
|
|||||||
import { useNotesSettings } from '@renderer/hooks/useNotesSettings'
|
import { useNotesSettings } from '@renderer/hooks/useNotesSettings'
|
||||||
import { initWorkSpace } from '@renderer/services/NotesService'
|
import { initWorkSpace } from '@renderer/services/NotesService'
|
||||||
import type { EditorView } from '@renderer/types'
|
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 { FolderOpen } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@ -50,7 +50,7 @@ const NotesSettings: FC = () => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to select directory:', error as 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 {
|
} finally {
|
||||||
setIsSelecting(false)
|
setIsSelecting(false)
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ const NotesSettings: FC = () => {
|
|||||||
|
|
||||||
const handleApplyPath = async () => {
|
const handleApplyPath = async () => {
|
||||||
if (!tempPath) {
|
if (!tempPath) {
|
||||||
message.error(t('notes.settings.data.path_required'))
|
window.toast.error(t('notes.settings.data.path_required'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ const NotesSettings: FC = () => {
|
|||||||
const isValidDir = await window.api.file.validateNotesDirectory(tempPath)
|
const isValidDir = await window.api.file.validateNotesDirectory(tempPath)
|
||||||
|
|
||||||
if (!isValidDir) {
|
if (!isValidDir) {
|
||||||
message.error(t('notes.settings.data.invalid_directory'))
|
window.toast.error(t('notes.settings.data.invalid_directory'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
|||||||
import type { Model, ModelCapability, ModelType, Provider } from '@renderer/types'
|
import type { Model, ModelCapability, ModelType, Provider } from '@renderer/types'
|
||||||
import { getDefaultGroupName, getDifference, getUnion, uniqueObjectArray } from '@renderer/utils'
|
import { getDefaultGroupName, getDifference, getUnion, uniqueObjectArray } from '@renderer/utils'
|
||||||
import type { ModalProps } from 'antd'
|
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 { cloneDeep } from 'lodash'
|
||||||
import { ChevronDown, ChevronUp, RotateCcw, SaveIcon } from 'lucide-react'
|
import { ChevronDown, ChevronUp, RotateCcw, SaveIcon } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
@ -281,7 +281,7 @@ const ModelEditContent: FC<ModelEditContentProps & ModalProps> = ({ provider, mo
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const val = form.getFieldValue('name')
|
const val = form.getFieldValue('name')
|
||||||
navigator.clipboard.writeText((val.id || model.id) as string)
|
navigator.clipboard.writeText((val.id || model.id) as string)
|
||||||
message.success(t('message.copied'))
|
window.toast.success(t('message.copied'))
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { getToastUtilities } from '@cherrystudio/ui'
|
||||||
import { AppLogo } from '@renderer/config/env'
|
import { AppLogo } from '@renderer/config/env'
|
||||||
import { loggerService } from '@renderer/services/LoggerService'
|
import { loggerService } from '@renderer/services/LoggerService'
|
||||||
import { IpcChannel } from '@shared/IpcChannel'
|
import { IpcChannel } from '@shared/IpcChannel'
|
||||||
@ -33,6 +34,10 @@ const MigrateApp: React.FC = () => {
|
|||||||
message: 'Ready to start data migration'
|
message: 'Ready to start data migration'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.toast = getToastUtilities()
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Listen for progress updates
|
// Listen for progress updates
|
||||||
const handleProgress = (_: any, progressData: MigrationProgress) => {
|
const handleProgress = (_: any, progressData: MigrationProgress) => {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
import { getToastUtilities } from '@cherrystudio/ui'
|
||||||
import { AppLogo } from '@renderer/config/env'
|
import { AppLogo } from '@renderer/config/env'
|
||||||
import { usePreference } from '@renderer/data/hooks/usePreference'
|
import { usePreference } from '@renderer/data/hooks/usePreference'
|
||||||
import { loggerService } from '@renderer/services/LoggerService'
|
import { loggerService } from '@renderer/services/LoggerService'
|
||||||
import { ThemeMode } from '@shared/data/preference/preferenceTypes'
|
import { ThemeMode } from '@shared/data/preference/preferenceTypes'
|
||||||
import { Button, Card, Col, Divider, Layout, Row, Space, Tabs, Typography } from 'antd'
|
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 { 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 styled from 'styled-components'
|
||||||
|
|
||||||
import CacheAdvancedTests from './components/CacheAdvancedTests'
|
import CacheAdvancedTests from './components/CacheAdvancedTests'
|
||||||
@ -54,6 +55,10 @@ const TestApp: React.FC = () => {
|
|||||||
return Math.floor(Date.now() / 1000) % 100
|
return Math.floor(Date.now() / 1000) % 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.toast = getToastUtilities()
|
||||||
|
}, [])
|
||||||
|
|
||||||
const windowNumber = getWindowNumber()
|
const windowNumber = getWindowNumber()
|
||||||
|
|
||||||
// Add theme preference monitoring for UI changes
|
// Add theme preference monitoring for UI changes
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import '@renderer/databases'
|
import '@renderer/databases'
|
||||||
|
|
||||||
|
import { getToastUtilities } from '@cherrystudio/ui'
|
||||||
import { usePreference } from '@data/hooks/usePreference'
|
import { usePreference } from '@data/hooks/usePreference'
|
||||||
import { ErrorBoundary } from '@renderer/components/ErrorBoundary'
|
import { ErrorBoundary } from '@renderer/components/ErrorBoundary'
|
||||||
import { ToastPortal } from '@renderer/components/ToastPortal'
|
import { ToastPortal } from '@renderer/components/ToastPortal'
|
||||||
import { getToastUtilities } from '@renderer/components/TopView/toast'
|
|
||||||
import { HeroUIProvider } from '@renderer/context/HeroUIProvider'
|
import { HeroUIProvider } from '@renderer/context/HeroUIProvider'
|
||||||
import store, { persistor } from '@renderer/store'
|
import store, { persistor } from '@renderer/store'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import '@renderer/assets/styles/index.css'
|
|||||||
import '@renderer/assets/styles/tailwind.css'
|
import '@renderer/assets/styles/tailwind.css'
|
||||||
import '@ant-design/v5-patch-for-react-19'
|
import '@ant-design/v5-patch-for-react-19'
|
||||||
|
|
||||||
|
import { getToastUtilities } from '@cherrystudio/ui'
|
||||||
import { preferenceService } from '@data/PreferenceService'
|
import { preferenceService } from '@data/PreferenceService'
|
||||||
import KeyvStorage from '@kangfenmao/keyv-storage'
|
import KeyvStorage from '@kangfenmao/keyv-storage'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { ToastPortal } from '@renderer/components/ToastPortal'
|
import { ToastPortal } from '@renderer/components/ToastPortal'
|
||||||
import { getToastUtilities } from '@renderer/components/TopView/toast'
|
|
||||||
import AntdProvider from '@renderer/context/AntdProvider'
|
import AntdProvider from '@renderer/context/AntdProvider'
|
||||||
import { CodeStyleProvider } from '@renderer/context/CodeStyleProvider'
|
import { CodeStyleProvider } from '@renderer/context/CodeStyleProvider'
|
||||||
import { HeroUIProvider } from '@renderer/context/HeroUIProvider'
|
import { HeroUIProvider } from '@renderer/context/HeroUIProvider'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user