mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-10 07:19:02 +08:00
feat: update TypeScript configuration and enhance discover page layout
- Added support for the @modelcontextprotocol/sdk in tsconfig.node.json. - Updated import paths in provider.ts to include .js extensions. - Enhanced AgentsPage layout by integrating Navbar and Input components. - Refactored DiscoverPage to remove DialogManagerProvider and related components, simplifying the structure. - Removed unused dialog components and hooks to streamline the discover functionality. - Minor adjustments to AssistantSettings and Vercel tabs for improved code clarity.
This commit is contained in:
parent
b7d9949832
commit
c6d5faff73
@ -1,8 +1,12 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
import { getConfigDir } from '@main/utils/file'
|
import { getConfigDir } from '@main/utils/file'
|
||||||
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth'
|
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'
|
||||||
import { OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth'
|
import {
|
||||||
|
OAuthClientInformation,
|
||||||
|
OAuthClientInformationFull,
|
||||||
|
OAuthTokens
|
||||||
|
} from '@modelcontextprotocol/sdk/shared/auth.js'
|
||||||
import Logger from 'electron-log'
|
import Logger from 'electron-log'
|
||||||
import open from 'open'
|
import open from 'open'
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { ImportOutlined, PlusOutlined } from '@ant-design/icons'
|
import { ImportOutlined, PlusOutlined } from '@ant-design/icons'
|
||||||
|
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||||
import CustomTag from '@renderer/components/CustomTag'
|
import CustomTag from '@renderer/components/CustomTag'
|
||||||
import ListItem from '@renderer/components/ListItem'
|
import ListItem from '@renderer/components/ListItem'
|
||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
@ -6,8 +7,9 @@ import { useAgents } from '@renderer/hooks/useAgents'
|
|||||||
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
|
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
|
||||||
import { Agent } from '@renderer/types'
|
import { Agent } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
import { Button, Empty, Flex } from 'antd'
|
import { Button, Empty, Flex, Input } from 'antd'
|
||||||
import { omit } from 'lodash'
|
import { omit } from 'lodash'
|
||||||
|
import { Search } from 'lucide-react'
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
@ -150,7 +152,7 @@ const AgentsPage: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{/* <Navbar>
|
<Navbar>
|
||||||
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'space-between' }}>
|
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'space-between' }}>
|
||||||
{t('agents.title')}
|
{t('agents.title')}
|
||||||
<Input
|
<Input
|
||||||
@ -169,7 +171,7 @@ const AgentsPage: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<div style={{ width: 80 }} />
|
<div style={{ width: 80 }} />
|
||||||
</NavbarCenter>
|
</NavbarCenter>
|
||||||
</Navbar> */}
|
</Navbar>
|
||||||
|
|
||||||
<Main id="content-container">
|
<Main id="content-container">
|
||||||
<AgentsGroupList>
|
<AgentsGroupList>
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
import { CherryStoreItem } from '@renderer/types/cherryStore'
|
|
||||||
import { createContext, ReactNode, use, useState } from 'react'
|
|
||||||
|
|
||||||
interface ActiveDialog {
|
|
||||||
type: string
|
|
||||||
item: CherryStoreItem
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DialogManagerContextType {
|
|
||||||
activeDialog: ActiveDialog | null
|
|
||||||
openDialog: (type: string, item: CherryStoreItem) => void
|
|
||||||
closeDialog: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const DialogManagerContext = createContext<DialogManagerContextType | undefined>(undefined)
|
|
||||||
|
|
||||||
export const DialogManagerProvider = ({ children }: { children: ReactNode }) => {
|
|
||||||
const [activeDialog, setActiveDialog] = useState<ActiveDialog | null>(null)
|
|
||||||
|
|
||||||
const openDialog = (type: string, item: CherryStoreItem) => {
|
|
||||||
setActiveDialog({ type, item })
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeDialog = () => {
|
|
||||||
setActiveDialog(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <DialogManagerContext value={{ activeDialog, openDialog, closeDialog }}>{children}</DialogManagerContext>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useDialogManager = (): DialogManagerContextType => {
|
|
||||||
const context = use(DialogManagerContext)
|
|
||||||
if (!context) {
|
|
||||||
// More robust check
|
|
||||||
throw new Error('useDialogManager must be used within a DialogManagerProvider')
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
@ -1,157 +0,0 @@
|
|||||||
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
|
|
||||||
import { Agent } from '@renderer/types'
|
|
||||||
import { CherryStoreItem, CherryStoreType } from '@renderer/types/cherryStore'
|
|
||||||
import { Badge } from '@renderer/ui/badge'
|
|
||||||
import { Button } from '@renderer/ui/button'
|
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@renderer/ui/dialog'
|
|
||||||
import { Download } from 'lucide-react'
|
|
||||||
import ReactMarkdown from 'react-markdown'
|
|
||||||
import { v4 as uuid } from 'uuid'
|
|
||||||
|
|
||||||
export default function InstallDialog({
|
|
||||||
item,
|
|
||||||
isOpen,
|
|
||||||
onClose
|
|
||||||
}: {
|
|
||||||
item: CherryStoreItem | null
|
|
||||||
isOpen: boolean
|
|
||||||
onClose: () => void
|
|
||||||
}) {
|
|
||||||
if (!item) return null
|
|
||||||
|
|
||||||
const handleInstall = () => {
|
|
||||||
switch (item.type) {
|
|
||||||
case CherryStoreType.ASSISTANT: {
|
|
||||||
const getAgentFromSystemAgent = (agent) => {
|
|
||||||
return {
|
|
||||||
prompt: agent.prompt,
|
|
||||||
description: agent.description,
|
|
||||||
emoji: agent.icon,
|
|
||||||
name: agent.title,
|
|
||||||
id: uuid(),
|
|
||||||
topics: [],
|
|
||||||
type: 'agent'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createAssistantFromAgent(getAgentFromSystemAgent(item) as unknown as Agent)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
onClose()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
|
||||||
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-4xl">
|
|
||||||
<DialogHeader className="flex flex-row items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<DialogTitle className="flex items-center space-x-1 text-xl">
|
|
||||||
{item.title}
|
|
||||||
<Badge variant="outline" className="ml-2">
|
|
||||||
{item.type}
|
|
||||||
</Badge>
|
|
||||||
{item.icon && <span className="text-2xl">{item.icon}</span>}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription className="mt-1 text-base">{item.description}</DialogDescription>
|
|
||||||
</div>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="mt-4 grid grid-cols-1 gap-6 md:grid-cols-3">
|
|
||||||
<div className="md:col-span-2">
|
|
||||||
<div className="mt-4 rounded-lg border bg-card p-6 text-card-foreground shadow-sm">
|
|
||||||
<ReactMarkdown>{item.prompt}</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* {item.requirements && (
|
|
||||||
<div className="mt-4 rounded-lg border bg-card p-6 text-card-foreground shadow-sm">
|
|
||||||
<h3 className="mb-4 text-lg font-semibold">功能要求</h3>
|
|
||||||
<ul className="space-y-2">
|
|
||||||
{item.requirements.map((req: string, index: number) => (
|
|
||||||
<li key={index} className="flex items-start">
|
|
||||||
<Check className="mr-2 mt-0.5 h-4 w-4 text-green-500" />
|
|
||||||
<span>{req}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="overflow-hidden">
|
|
||||||
{item.image && (
|
|
||||||
<div className="aspect-square w-full overflow-hidden rounded-lg border bg-muted">
|
|
||||||
<img src={item.image || '/placeholder.svg'} alt={item.title} className="h-full w-full object-cover" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="rounded-lg border bg-card p-4 text-card-foreground shadow-sm">
|
|
||||||
{/* <div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium">评分</span>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Star className="mr-1 h-4 w-4 fill-primary text-primary" />
|
|
||||||
<span className="font-medium">{item.rating}</span>
|
|
||||||
<span className="ml-1 text-xs text-muted-foreground">({item.downloads})</span>
|
|
||||||
</div>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<div className="mt-3 flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium">版本</span>
|
|
||||||
{/* <span>{item.version}</span> */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-3 flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium">更新日期</span>
|
|
||||||
{/* <span>{item.lastUpdated}</span> */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-3 flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium">开发者</span>
|
|
||||||
<span>{item.author}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* {item.type !== CherryStoreType.ASSISTANT && ( */}
|
|
||||||
<div className="mt-4">
|
|
||||||
<Button className="w-full" onClick={handleInstall}>
|
|
||||||
<Download className="mr-2 h-4 w-4 text-white! dark:text-black!" />
|
|
||||||
添加到助手
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{/* )} */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2 rounded-lg border bg-card p-4 text-card-foreground shadow-sm">
|
|
||||||
<h3 className="mb-2 text-sm font-medium">标签</h3>
|
|
||||||
<div className="flex flex-wrap gap-1">
|
|
||||||
{item.tags.map((tag: string) => (
|
|
||||||
<Badge key={tag} variant="secondary" className="text-xs">
|
|
||||||
{tag}
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<h3 className="mb-2 text-sm font-medium">分类</h3>
|
|
||||||
<Badge variant="secondary" className="text-xs">
|
|
||||||
{item.type}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <DialogFooter className="mt-6 flex sm:justify-between">
|
|
||||||
<Button variant="outline" onClick={onClose}>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
<Button>
|
|
||||||
<Download className="mr-2 h-4 w-4 text-white! dark:text-black!" />
|
|
||||||
添加到助手
|
|
||||||
</Button>
|
|
||||||
</DialogFooter> */}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import { useDialogManager } from './DialogManagerContext'
|
|
||||||
import InstallDialog from './InstallDialog'
|
|
||||||
// Import other dialog components here as they are created
|
|
||||||
// e.g., import ItemDetailsDialog from './ItemDetailsDialog';
|
|
||||||
|
|
||||||
export default function DialogManager() {
|
|
||||||
// Renamed component for clarity
|
|
||||||
const { activeDialog, closeDialog } = useDialogManager()
|
|
||||||
|
|
||||||
if (!activeDialog) {
|
|
||||||
return null // No dialog is active
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (activeDialog.type) {
|
|
||||||
case 'install':
|
|
||||||
return (
|
|
||||||
<InstallDialog
|
|
||||||
item={activeDialog.item} // item is guaranteed by activeDialog structure
|
|
||||||
isOpen={true} // If we are rendering, it means this dialog should be open
|
|
||||||
onClose={closeDialog}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
// case 'viewDetails':
|
|
||||||
// return (
|
|
||||||
// <ItemDetailsDialog
|
|
||||||
// item={activeDialog.item}
|
|
||||||
// isOpen={true}
|
|
||||||
// onClose={closeDialog}
|
|
||||||
// />
|
|
||||||
// );
|
|
||||||
// Add more cases for other dialog types here
|
|
||||||
default:
|
|
||||||
console.warn('Unknown dialog type:', activeDialog.type)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import { CherryStoreItem } from '@renderer/types/cherryStore'
|
|
||||||
import { useMemo } from 'react'
|
|
||||||
|
|
||||||
// 假设 Item 类型定义,您可以从 store_list.json 的结构推断或在项目中共享
|
|
||||||
// 如果没有明确的类型定义,可以使用 any,但强烈建议定义类型
|
|
||||||
|
|
||||||
export function useFilteredStoreItems(
|
|
||||||
storeList: CherryStoreItem[],
|
|
||||||
searchQuery: string,
|
|
||||||
selectedCategory: string,
|
|
||||||
selectedSubcategory: string
|
|
||||||
) {
|
|
||||||
return useMemo(() => {
|
|
||||||
if (!storeList) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return storeList.filter((item) => {
|
|
||||||
const lowerSearchQuery = searchQuery.toLowerCase()
|
|
||||||
const matchesSearch =
|
|
||||||
searchQuery === '' ||
|
|
||||||
item.title.toLowerCase().includes(lowerSearchQuery) ||
|
|
||||||
item.description.toLowerCase().includes(lowerSearchQuery) ||
|
|
||||||
item.author.toLowerCase().includes(lowerSearchQuery) ||
|
|
||||||
item.tags.some((tag) => tag.toLowerCase().includes(lowerSearchQuery))
|
|
||||||
|
|
||||||
let matchesCategory = false
|
|
||||||
if (selectedCategory === 'all') {
|
|
||||||
matchesCategory = true
|
|
||||||
} else if (['featured', 'new', 'top'].includes(selectedCategory)) {
|
|
||||||
// 当前的筛选逻辑中 'featured', 'new', 'top' 是特殊处理的
|
|
||||||
// 'new' 和 'top' 还没有具体的实现逻辑,这里保持和原组件一致
|
|
||||||
if (selectedCategory === 'featured') {
|
|
||||||
matchesCategory = item.featured === true
|
|
||||||
} else {
|
|
||||||
// 如果 selectedCategory 是 'new' 或 'top' 但不是 'featured'
|
|
||||||
// 并且 item 没有 .featured = true, 那么 matchesCategory 仍然是 false
|
|
||||||
// 这可能需要根据实际需求调整,例如:
|
|
||||||
// if (selectedCategory === 'new') matchesCategory = item.isNew === true; (假设有 isNew 字段)
|
|
||||||
// if (selectedCategory === 'top') matchesCategory = item.isTop === true; (假设有 isTop 字段)
|
|
||||||
// 为了保持和原逻辑一致,这里暂时不修改这部分行为,但提示您可能需要完善
|
|
||||||
matchesCategory = item.featured === true && ['featured'].includes(selectedCategory) // 或者更复杂的逻辑
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
matchesCategory = item.categoryId === selectedCategory
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchesSubcategory =
|
|
||||||
['all', 'featured', 'new', 'top'].includes(selectedCategory) || // If a special category is selected, subcategory filter might be bypassed or handled differently
|
|
||||||
selectedSubcategory === 'all' ||
|
|
||||||
item.subcategoryId === selectedSubcategory
|
|
||||||
|
|
||||||
return matchesSearch && matchesCategory && matchesSubcategory
|
|
||||||
})
|
|
||||||
}, [storeList, searchQuery, selectedCategory, selectedSubcategory])
|
|
||||||
}
|
|
||||||
@ -6,8 +6,6 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
// Import Context and the main Dialog Manager component
|
// Import Context and the main Dialog Manager component
|
||||||
import { DialogManagerProvider } from './components/dialog/DialogManagerContext'
|
|
||||||
import Dialogs from './components/dialog/index'
|
|
||||||
import DiscoverContent from './components/DiscoverContent' // Removed DiscoverContent import
|
import DiscoverContent from './components/DiscoverContent' // Removed DiscoverContent import
|
||||||
import DiscoverSidebar from './components/DiscoverSidebar'
|
import DiscoverSidebar from './components/DiscoverSidebar'
|
||||||
import { InternalCategory, useDiscoverCategories } from './hooks/useDiscoverCategories'
|
import { InternalCategory, useDiscoverCategories } from './hooks/useDiscoverCategories'
|
||||||
@ -45,7 +43,7 @@ export default function DiscoverPage() {
|
|||||||
const vercelTabsData = adaptCategoriesForVercelTabs(categories)
|
const vercelTabsData = adaptCategoriesForVercelTabs(categories)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogManagerProvider>
|
<div>
|
||||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||||
<Navbar className="h-auto flex-shrink-0">
|
<Navbar className="h-auto flex-shrink-0">
|
||||||
<NavbarCenter>{t('discover.title')}</NavbarCenter>
|
<NavbarCenter>{t('discover.title')}</NavbarCenter>
|
||||||
@ -79,8 +77,7 @@ export default function DiscoverPage() {
|
|||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<Dialogs />
|
|
||||||
</div>
|
</div>
|
||||||
</DialogManagerProvider>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
|||||||
<StyledModal
|
<StyledModal
|
||||||
open={open}
|
open={open}
|
||||||
onOk={onOk}
|
onOk={onOk}
|
||||||
onClose={onCancel}
|
// onClose={onCancel}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
afterClose={afterClose}
|
afterClose={afterClose}
|
||||||
footer={null}
|
footer={null}
|
||||||
|
|||||||
@ -87,7 +87,9 @@ const Tabs = ({
|
|||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<div
|
<div
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
ref={(el) => (tabRefs.current[index] = el)}
|
ref={(el) => {
|
||||||
|
tabRefs.current[index] = el
|
||||||
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-[30px] cursor-pointer px-3 py-2 transition-colors duration-300',
|
'h-[30px] cursor-pointer px-3 py-2 transition-colors duration-300',
|
||||||
index === activeIndex ? 'text-[#0e0e10] dark:text-white' : 'text-[#0e0f1199] dark:text-[#ffffff99]'
|
index === activeIndex ? 'text-[#0e0e10] dark:text-white' : 'text-[#0e0f1199] dark:text-[#ffffff99]'
|
||||||
|
|||||||
@ -19,7 +19,8 @@
|
|||||||
"@main/*": ["src/main/*"],
|
"@main/*": ["src/main/*"],
|
||||||
"@types": ["src/renderer/src/types/index.ts"],
|
"@types": ["src/renderer/src/types/index.ts"],
|
||||||
"@shared/*": ["packages/shared/*"],
|
"@shared/*": ["packages/shared/*"],
|
||||||
"@renderer/*": ["src/renderer/src/*"]
|
"@renderer/*": ["src/renderer/src/*"],
|
||||||
|
"@modelcontextprotocol/sdk/*": ["node_modules/@modelcontextprotocol/sdk/dist/esm/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user