mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 10:40:07 +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 { getConfigDir } from '@main/utils/file'
|
||||
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth'
|
||||
import { OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth'
|
||||
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'
|
||||
import {
|
||||
OAuthClientInformation,
|
||||
OAuthClientInformationFull,
|
||||
OAuthTokens
|
||||
} from '@modelcontextprotocol/sdk/shared/auth.js'
|
||||
import Logger from 'electron-log'
|
||||
import open from 'open'
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { ImportOutlined, PlusOutlined } from '@ant-design/icons'
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import CustomTag from '@renderer/components/CustomTag'
|
||||
import ListItem from '@renderer/components/ListItem'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
@ -6,8 +7,9 @@ import { useAgents } from '@renderer/hooks/useAgents'
|
||||
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
|
||||
import { Agent } from '@renderer/types'
|
||||
import { uuid } from '@renderer/utils'
|
||||
import { Button, Empty, Flex } from 'antd'
|
||||
import { Button, Empty, Flex, Input } from 'antd'
|
||||
import { omit } from 'lodash'
|
||||
import { Search } from 'lucide-react'
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
@ -150,7 +152,7 @@ const AgentsPage: FC = () => {
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{/* <Navbar>
|
||||
<Navbar>
|
||||
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'space-between' }}>
|
||||
{t('agents.title')}
|
||||
<Input
|
||||
@ -169,7 +171,7 @@ const AgentsPage: FC = () => {
|
||||
/>
|
||||
<div style={{ width: 80 }} />
|
||||
</NavbarCenter>
|
||||
</Navbar> */}
|
||||
</Navbar>
|
||||
|
||||
<Main id="content-container">
|
||||
<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 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 DiscoverSidebar from './components/DiscoverSidebar'
|
||||
import { InternalCategory, useDiscoverCategories } from './hooks/useDiscoverCategories'
|
||||
@ -45,7 +43,7 @@ export default function DiscoverPage() {
|
||||
const vercelTabsData = adaptCategoriesForVercelTabs(categories)
|
||||
|
||||
return (
|
||||
<DialogManagerProvider>
|
||||
<div>
|
||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||
<Navbar className="h-auto flex-shrink-0">
|
||||
<NavbarCenter>{t('discover.title')}</NavbarCenter>
|
||||
@ -79,8 +77,7 @@ export default function DiscoverPage() {
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
<Dialogs />
|
||||
</div>
|
||||
</DialogManagerProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
||||
<StyledModal
|
||||
open={open}
|
||||
onOk={onOk}
|
||||
onClose={onCancel}
|
||||
// onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={afterClose}
|
||||
footer={null}
|
||||
|
||||
@ -87,7 +87,9 @@ const Tabs = ({
|
||||
{tabs.map((tab, index) => (
|
||||
<div
|
||||
key={tab.id}
|
||||
ref={(el) => (tabRefs.current[index] = el)}
|
||||
ref={(el) => {
|
||||
tabRefs.current[index] = el
|
||||
}}
|
||||
className={cn(
|
||||
'h-[30px] cursor-pointer px-3 py-2 transition-colors duration-300',
|
||||
index === activeIndex ? 'text-[#0e0e10] dark:text-white' : 'text-[#0e0f1199] dark:text-[#ffffff99]'
|
||||
|
||||
@ -19,7 +19,8 @@
|
||||
"@main/*": ["src/main/*"],
|
||||
"@types": ["src/renderer/src/types/index.ts"],
|
||||
"@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