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:
MyPrototypeWhat 2025-06-09 15:22:25 +08:00
parent b7d9949832
commit c6d5faff73
10 changed files with 19 additions and 299 deletions

View File

@ -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'

View File

@ -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>

View File

@ -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
}

View File

@ -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>
)
}

View File

@ -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
}
}

View File

@ -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])
}

View File

@ -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>
)
}

View File

@ -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}

View File

@ -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]'

View File

@ -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/*"]
}
}
}