mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 11:44:28 +08:00
feat: update store components and add dialog management functionality
- Updated package.json to use the latest version of the 'motion' library. - Refactored store components to improve organization and user experience, including the addition of AssistantCard and MiniAppCard components. - Introduced a DialogManager for handling dialog states and interactions. - Enhanced StoreContent and StoreSidebar components to support new item types and improved layout. - Added new JSON data for mini-apps and updated store categories for better accessibility.
This commit is contained in:
parent
ef16558947
commit
80289f1dc3
@ -185,7 +185,7 @@
|
||||
"lru-cache": "^11.1.0",
|
||||
"lucide-react": "^0.509.0",
|
||||
"mime": "^4.0.4",
|
||||
"motion": "^12.11.0",
|
||||
"motion": "^12.12.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"npx-scope-finder": "^1.2.0",
|
||||
"openai": "patch:openai@npm%3A4.96.0#~/.yarn/patches/openai-npm-4.96.0-0665b05cb9.patch",
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "assistant",
|
||||
"id": "Assistant",
|
||||
"title": "助手",
|
||||
"items": [
|
||||
{
|
||||
@ -192,12 +192,12 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mini-app",
|
||||
"id": "Mini-App",
|
||||
"title": "小程序",
|
||||
"items": []
|
||||
},
|
||||
{
|
||||
"id": "knowledge",
|
||||
"id": "Knowledge",
|
||||
"title": "知识库",
|
||||
"items": [
|
||||
{
|
||||
@ -243,7 +243,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mcp-server",
|
||||
"id": "MCP-Server",
|
||||
"title": "MCP 服务器",
|
||||
"items": [
|
||||
{
|
||||
@ -333,12 +333,12 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "model-provider",
|
||||
"id": "Model-Provider",
|
||||
"title": "模型服务",
|
||||
"items": []
|
||||
},
|
||||
{
|
||||
"id": "agent",
|
||||
"id": "Agent",
|
||||
"title": "智能体",
|
||||
"items": []
|
||||
}
|
||||
|
||||
691
resources/data/store_list_mini-app.json
Normal file
691
resources/data/store_list_mini-app.json
Normal file
@ -0,0 +1,691 @@
|
||||
[
|
||||
{
|
||||
"id": "mini-app-openai",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "ChatGPT",
|
||||
"description": "",
|
||||
"author": "openai",
|
||||
"image": "OpenAiProviderLogo",
|
||||
"tags": [],
|
||||
"url": "https://chatgpt.com/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-gemini",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Gemini",
|
||||
"description": "",
|
||||
"author": "gemini",
|
||||
"image": "GeminiAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://gemini.google.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-silicon",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "SiliconFlow",
|
||||
"description": "",
|
||||
"author": "silicon",
|
||||
"image": "SiliconFlowProviderLogo",
|
||||
"tags": [],
|
||||
"url": "https://cloud.siliconflow.cn/playground/chat"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-deepseek",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "DeepSeek",
|
||||
"description": "",
|
||||
"author": "deepseek",
|
||||
"image": "DeepSeekProviderLogo",
|
||||
"tags": [],
|
||||
"url": "https://chat.deepseek.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-yi",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "万知",
|
||||
"description": "",
|
||||
"author": "yi",
|
||||
"image": "WanZhiAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.wanzhi.com/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-zhipu",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "智谱清言",
|
||||
"description": "",
|
||||
"author": "zhipu",
|
||||
"image": "ZhipuProviderLogo",
|
||||
"tags": [],
|
||||
"url": "https://chatglm.cn/main/alltoolsdetail"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-moonshot",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Kimi",
|
||||
"description": "",
|
||||
"author": "moonshot",
|
||||
"image": "KimiAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://kimi.moonshot.cn/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-baichuan",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "百小应",
|
||||
"description": "",
|
||||
"author": "baichuan",
|
||||
"image": "BaicuanAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://ying.baichuan-ai.com/chat"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-dashscope",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "通义千问",
|
||||
"description": "",
|
||||
"author": "dashscope",
|
||||
"image": "QwenModelLogo",
|
||||
"tags": [],
|
||||
"url": "https://tongyi.aliyun.com/qianwen/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-stepfun",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "跃问",
|
||||
"description": "",
|
||||
"author": "stepfun",
|
||||
"image": "YuewenAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://yuewen.cn/chats/new",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-doubao",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "豆包",
|
||||
"description": "",
|
||||
"author": "doubao",
|
||||
"image": "DoubaoAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.doubao.com/chat/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-cici",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Cici",
|
||||
"description": "",
|
||||
"author": "cici",
|
||||
"image": "CiciAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.cici.com/chat/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-minimax",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "海螺",
|
||||
"description": "",
|
||||
"author": "minimax",
|
||||
"image": "HailuoModelLogo",
|
||||
"tags": [],
|
||||
"url": "https://hailuoai.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-groq",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Groq",
|
||||
"description": "",
|
||||
"author": "groq",
|
||||
"image": "GroqProviderLogo",
|
||||
"tags": [],
|
||||
"url": "https://chat.groq.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-anthropic",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Claude",
|
||||
"description": "",
|
||||
"author": "anthropic",
|
||||
"image": "ClaudeAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://claude.ai/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-baidu-ai-chat",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "文心一言",
|
||||
"description": "",
|
||||
"author": "baidu-ai-chat",
|
||||
"image": "BaiduAiAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://yiyan.baidu.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-baidu-ai-search",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "百度AI搜索",
|
||||
"description": "",
|
||||
"author": "baidu-ai-search",
|
||||
"image": "BaiduAiSearchLogo",
|
||||
"tags": [],
|
||||
"url": "https://chat.baidu.com/",
|
||||
"bodered": true,
|
||||
"style": {
|
||||
"padding": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mini-app-tencent-yuanbao",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "腾讯元宝",
|
||||
"description": "",
|
||||
"author": "tencent-yuanbao",
|
||||
"image": "TencentYuanbaoAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://yuanbao.tencent.com/chat",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-sensetime-chat",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "商量",
|
||||
"description": "",
|
||||
"author": "sensetime-chat",
|
||||
"image": "SensetimeAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://chat.sensetime.com/wb/chat",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-spark-desk",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "SparkDesk",
|
||||
"description": "",
|
||||
"author": "spark-desk",
|
||||
"image": "SparkDeskAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://xinghuo.xfyun.cn/desk"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-metaso",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "秘塔AI搜索",
|
||||
"description": "",
|
||||
"author": "metaso",
|
||||
"image": "MetasoAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://metaso.cn/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-poe",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Poe",
|
||||
"description": "",
|
||||
"author": "poe",
|
||||
"image": "PoeAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://poe.com"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-perplexity",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Perplexity",
|
||||
"description": "",
|
||||
"author": "perplexity",
|
||||
"image": "PerplexityAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.perplexity.ai/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-devv",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "DEVV_",
|
||||
"description": "",
|
||||
"author": "devv",
|
||||
"image": "DevvAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://devv.ai/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-tiangong-ai",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "天工AI",
|
||||
"description": "",
|
||||
"author": "tiangong-ai",
|
||||
"image": "TiangongAiLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.tiangong.cn/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-hugging-chat",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "HuggingChat",
|
||||
"description": "",
|
||||
"author": "hugging-chat",
|
||||
"image": "HuggingChatLogo",
|
||||
"tags": [],
|
||||
"url": "https://huggingface.co/chat/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-Felo",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Felo",
|
||||
"description": "",
|
||||
"author": "Felo",
|
||||
"image": "FeloAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://felo.ai/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-duckduckgo",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "DuckDuckGo",
|
||||
"description": "",
|
||||
"author": "duckduckgo",
|
||||
"image": "DuckDuckGoAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://duck.ai"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-bolt",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "bolt",
|
||||
"description": "",
|
||||
"author": "bolt",
|
||||
"image": "BoltAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://bolt.new/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-nm",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "纳米AI",
|
||||
"description": "",
|
||||
"author": "nm",
|
||||
"image": "NamiAiLogo",
|
||||
"tags": [],
|
||||
"url": "https://bot.n.cn/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-nm-search",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "纳米AI搜索",
|
||||
"description": "",
|
||||
"author": "nm-search",
|
||||
"image": "NamiAiSearchLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.n.cn/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-thinkany",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "ThinkAny",
|
||||
"description": "",
|
||||
"author": "thinkany",
|
||||
"image": "ThinkAnyLogo",
|
||||
"tags": [],
|
||||
"url": "https://thinkany.ai/",
|
||||
"bodered": true,
|
||||
"style": {
|
||||
"padding": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mini-app-hika",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Hika",
|
||||
"description": "",
|
||||
"author": "hika",
|
||||
"image": "HikaLogo",
|
||||
"tags": [],
|
||||
"url": "https://hika.fyi/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-github-copilot",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "GitHub Copilot",
|
||||
"description": "",
|
||||
"author": "github-copilot",
|
||||
"image": "GithubCopilotLogo",
|
||||
"tags": [],
|
||||
"url": "https://github.com/copilot"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-genspark",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Genspark",
|
||||
"description": "",
|
||||
"author": "genspark",
|
||||
"image": "GensparkLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.genspark.ai/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-grok",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Grok",
|
||||
"description": "",
|
||||
"author": "grok",
|
||||
"image": "GrokAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://grok.com",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-grok-x",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Grok / X",
|
||||
"description": "",
|
||||
"author": "grok-x",
|
||||
"image": "GrokXAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://x.com/i/grok",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-qwenlm",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "QwenLM",
|
||||
"description": "",
|
||||
"author": "qwenlm",
|
||||
"image": "QwenlmAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://qwenlm.ai/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-flowith",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Flowith",
|
||||
"description": "",
|
||||
"author": "flowith",
|
||||
"image": "FlowithAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.flowith.io/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-3mintop",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "3MinTop",
|
||||
"description": "",
|
||||
"author": "3mintop",
|
||||
"image": "ThreeMinTopAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://3min.top",
|
||||
"bodered": false
|
||||
},
|
||||
{
|
||||
"id": "mini-app-aistudio",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "AI Studio",
|
||||
"description": "",
|
||||
"author": "aistudio",
|
||||
"image": "AIStudioLogo",
|
||||
"tags": [],
|
||||
"url": "https://aistudio.google.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-xiaoyi",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "小艺",
|
||||
"description": "",
|
||||
"author": "xiaoyi",
|
||||
"image": "XiaoYiAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://xiaoyi.huawei.com/chat/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-notebooklm",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "NotebookLM",
|
||||
"description": "",
|
||||
"author": "notebooklm",
|
||||
"image": "NotebookLMAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://notebooklm.google.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-coze",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Coze",
|
||||
"description": "",
|
||||
"author": "coze",
|
||||
"image": "CozeAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://www.coze.com/space",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-dify",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Dify",
|
||||
"description": "",
|
||||
"author": "dify",
|
||||
"image": "DifyAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://cloud.dify.ai/apps",
|
||||
"bodered": true,
|
||||
"style": {
|
||||
"padding": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mini-app-wpslingxi",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "WPS灵犀",
|
||||
"description": "",
|
||||
"author": "wpslingxi",
|
||||
"image": "WPSLingXiLogo",
|
||||
"tags": [],
|
||||
"url": "https://copilot.wps.cn/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-lechat",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "LeChat",
|
||||
"description": "",
|
||||
"author": "lechat",
|
||||
"image": "LeChatLogo",
|
||||
"tags": [],
|
||||
"url": "https://chat.mistral.ai/chat",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-abacus",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Abacus",
|
||||
"description": "",
|
||||
"author": "abacus",
|
||||
"image": "AbacusLogo",
|
||||
"tags": [],
|
||||
"url": "https://apps.abacus.ai/chatllm",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-lambdachat",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Lambda Chat",
|
||||
"description": "",
|
||||
"author": "lambdachat",
|
||||
"image": "LambdaChatLogo",
|
||||
"tags": [],
|
||||
"url": "https://lambda.chat/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-monica",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Monica",
|
||||
"description": "",
|
||||
"author": "monica",
|
||||
"image": "MonicaLogo",
|
||||
"tags": [],
|
||||
"url": "https://monica.im/home/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-you",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "You",
|
||||
"description": "",
|
||||
"author": "you",
|
||||
"image": "YouLogo",
|
||||
"tags": [],
|
||||
"url": "https://you.com/"
|
||||
},
|
||||
{
|
||||
"id": "mini-app-zhihu",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "知乎直答",
|
||||
"description": "",
|
||||
"author": "zhihu",
|
||||
"image": "ZhihuAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://zhida.zhihu.com/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-dangbei",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "当贝AI",
|
||||
"description": "",
|
||||
"author": "dangbei",
|
||||
"image": "DangbeiLogo",
|
||||
"tags": [],
|
||||
"url": "https://ai.dangbei.com/",
|
||||
"bodered": true
|
||||
},
|
||||
{
|
||||
"id": "mini-app-zai",
|
||||
"type": "Mini-App",
|
||||
"categoryId": "mini-app",
|
||||
"subcategoryId": "",
|
||||
"title": "Z.ai",
|
||||
"description": "",
|
||||
"author": "zai",
|
||||
"image": "ZaiAppLogo",
|
||||
"tags": [],
|
||||
"url": "https://chat.z.ai/",
|
||||
"bodered": true,
|
||||
"style": {
|
||||
"padding": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -0,0 +1,39 @@
|
||||
import { AssistantItem } from '@renderer/types/cherryStore'
|
||||
import { Badge } from '@renderer/ui/badge'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@renderer/ui/card'
|
||||
import { BlurFade } from '@renderer/ui/third-party/BlurFade'
|
||||
import { cn } from '@renderer/utils'
|
||||
|
||||
import { useDialogManager } from '../dialog/DialogManagerContext'
|
||||
|
||||
export default function AssistantCard({ item }: { item: AssistantItem }) {
|
||||
const { openDialog } = useDialogManager()
|
||||
|
||||
const handleCardClick = () => {
|
||||
openDialog('install', item)
|
||||
}
|
||||
|
||||
return (
|
||||
<BlurFade key={item.id} delay={0.2} inView className="mb-4 cursor-pointer">
|
||||
<Card className="overflow-hidden transition-transform hover:scale-105" onClick={handleCardClick}>
|
||||
<CardHeader className="p-0">
|
||||
<div className="flex h-full w-full items-center justify-center text-4xl" role="img" aria-label={item.title}>
|
||||
{item.icon}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className={cn('px-4', 'min-h-[120px]', 'space-y-1')}>
|
||||
<CardTitle className="line-clamp-2 text-base">{item.title}</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">{item.author}</p>
|
||||
<div className="space-x-2">
|
||||
{item.tags.map((tag) => (
|
||||
<Badge key={tag} variant="outline">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<p className={cn('mt-2 text-sm text-muted-foreground', 'line-clamp-4 xl:line-clamp-7')}>{item.description}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</BlurFade>
|
||||
)
|
||||
}
|
||||
@ -1,70 +1,46 @@
|
||||
import { CherryStoreItem } from '@renderer/types/cherryStore'
|
||||
import { Badge } from '@renderer/ui/badge'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@renderer/ui/card'
|
||||
import { BlurFade } from '@renderer/ui/third-party/BlurFade'
|
||||
import { cn } from '@renderer/utils'
|
||||
import { useState } from 'react'
|
||||
import { AssistantItem, CherryStoreItem, CherryStoreType, MiniAppItem } from '@renderer/types/cherryStore'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
|
||||
import { ItemDetailDialog } from './ItemDetailDialog'
|
||||
import AssistantCard from './Assistant/AssistantCard'
|
||||
import MiniAppCard from './MiniApp/MiniAppCard'
|
||||
|
||||
export function GridView({ items }: { items: CherryStoreItem[] }) {
|
||||
const [selectedItemForDetail, setSelectedItemForDetail] = useState<CherryStoreItem | null>(null)
|
||||
const [isDetailDialogOpen, setIsDetailDialogOpen] = useState(false)
|
||||
interface GridViewProps {
|
||||
items: CherryStoreItem[]
|
||||
selectedCategory: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
const handleCardClick = (item: CherryStoreItem) => {
|
||||
setSelectedItemForDetail(item)
|
||||
setIsDetailDialogOpen(true)
|
||||
const CardComponent = (selectedCategory: string, item: CherryStoreItem) => {
|
||||
switch (selectedCategory) {
|
||||
case CherryStoreType.ASSISTANT:
|
||||
return <AssistantCard item={item as AssistantItem} />
|
||||
case CherryStoreType.MINI_APP:
|
||||
return <MiniAppCard item={item as MiniAppItem} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function GridView({ items, selectedCategory }: GridViewProps) {
|
||||
const effectiveGridClass = useMemo(() => {
|
||||
let gridClass = 'columns-4 gap-4 '
|
||||
|
||||
switch (selectedCategory) {
|
||||
case CherryStoreType.ASSISTANT:
|
||||
gridClass += '2xl:columns-6'
|
||||
break
|
||||
case CherryStoreType.MINI_APP:
|
||||
gridClass = 'grid grid-cols-8 gap-4 2xl:grid-cols-10'
|
||||
break
|
||||
}
|
||||
return gridClass
|
||||
}, [selectedCategory])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="columns-4 gap-4">
|
||||
{items.map((item) => (
|
||||
<BlurFade key={item.id} delay={0.2} inView className="mb-4 cursor-pointer">
|
||||
<Card
|
||||
className="overflow-hidden transition-transform hover:scale-105"
|
||||
onClick={() => handleCardClick(item)}>
|
||||
<CardHeader className="p-0">
|
||||
{item.icon ? (
|
||||
<div
|
||||
className="flex h-full w-full items-center justify-center text-4xl"
|
||||
role="img"
|
||||
aria-label={item.title}>
|
||||
{item.icon}
|
||||
</div>
|
||||
) : (
|
||||
<div className={cn('w-full overflow-hidden bg-muted', 'aspect-square')}>
|
||||
<img
|
||||
src={item.image || '/placeholder.svg'}
|
||||
alt={item.title}
|
||||
className="h-full w-full object-cover transition-transform"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent className={cn('px-4', 'min-h-[120px]', 'space-y-1')}>
|
||||
<CardTitle className="line-clamp-2 text-base">{item.title}</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">{item.author}</p>
|
||||
<div className="space-x-2">
|
||||
{item.tags.map((tag) => (
|
||||
<Badge key={tag} variant="outline">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<p className={cn('mt-2 text-sm text-muted-foreground', 'line-clamp-4 xl:line-clamp-7')}>
|
||||
{item.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</BlurFade>
|
||||
))}
|
||||
</div>
|
||||
<ItemDetailDialog
|
||||
item={selectedItemForDetail}
|
||||
isOpen={isDetailDialogOpen}
|
||||
onClose={() => setIsDetailDialogOpen(false)}
|
||||
/>
|
||||
</>
|
||||
<div className={effectiveGridClass}>
|
||||
{items.map((item) => (
|
||||
<Fragment key={item.id}>{CardComponent(selectedCategory, item)}</Fragment>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { Badge } from '@renderer/ui/badge'
|
||||
import { Card } from '@renderer/ui/card'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { ItemDetailDialog } from './ItemDetailDialog'
|
||||
// import { ItemDetailDialog } from './ItemDetailDialog'
|
||||
|
||||
export function ListView({ items }: { items: CherryStoreItem[] }) {
|
||||
const [selectedItemForDetail, setSelectedItemForDetail] = useState<CherryStoreItem | null>(null)
|
||||
@ -73,11 +73,11 @@ export function ListView({ items }: { items: CherryStoreItem[] }) {
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<ItemDetailDialog
|
||||
{/* <ItemDetailDialog
|
||||
item={selectedItemForDetail}
|
||||
isOpen={isDetailDialogOpen}
|
||||
onClose={() => setIsDetailDialogOpen(false)}
|
||||
/>
|
||||
/> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
|
||||
import { MiniAppItem } from '@renderer/types/cherryStore'
|
||||
import { BlurFade } from '@renderer/ui/third-party/BlurFade'
|
||||
|
||||
import logoList from './logoList'
|
||||
export default function MiniAppCard({ item }: { item: MiniAppItem }) {
|
||||
const { openMinappKeepAlive } = useMinappPopup()
|
||||
const handleClick = () => {
|
||||
openMinappKeepAlive({
|
||||
id: item.id,
|
||||
name: item.title,
|
||||
url: item.url,
|
||||
logo: item.image,
|
||||
style: item.style
|
||||
})
|
||||
}
|
||||
return (
|
||||
<BlurFade key={item.id} delay={0.2} inView className="mb-4 cursor-pointer">
|
||||
<div className="flex h-full w-full flex-col items-center justify-between" onClick={handleClick}>
|
||||
<img src={logoList[item.image]} alt={item.title} className="w-full rounded-2xl" style={item.style} />
|
||||
<div className="mt-2 flex flex-col items-center justify-center">
|
||||
<p className="text-base text-[clamp(12px,1.1vw,16px)] font-medium text-muted-foreground">{item.title}</p>
|
||||
</div>
|
||||
</div>
|
||||
</BlurFade>
|
||||
)
|
||||
}
|
||||
111
src/renderer/src/pages/store/components/MiniApp/logoList.ts
Normal file
111
src/renderer/src/pages/store/components/MiniApp/logoList.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import ThreeMinTopAppLogo from '@renderer/assets/images/apps/3mintop.png?url'
|
||||
import AbacusLogo from '@renderer/assets/images/apps/abacus.webp?url'
|
||||
import AIStudioLogo from '@renderer/assets/images/apps/aistudio.svg?url'
|
||||
import BaiduAiAppLogo from '@renderer/assets/images/apps/baidu-ai.png?url'
|
||||
import BaiduAiSearchLogo from '@renderer/assets/images/apps/baidu-ai-search.webp?url'
|
||||
import BaicuanAppLogo from '@renderer/assets/images/apps/baixiaoying.webp?url'
|
||||
import BoltAppLogo from '@renderer/assets/images/apps/bolt.svg?url'
|
||||
import CiciAppLogo from '@renderer/assets/images/apps/cici.webp?url'
|
||||
import CozeAppLogo from '@renderer/assets/images/apps/coze.webp?url'
|
||||
import DangbeiLogo from '@renderer/assets/images/apps/dangbei.jpg?url'
|
||||
import DevvAppLogo from '@renderer/assets/images/apps/devv.png?url'
|
||||
import DifyAppLogo from '@renderer/assets/images/apps/dify.svg?url'
|
||||
import DoubaoAppLogo from '@renderer/assets/images/apps/doubao.png?url'
|
||||
import DuckDuckGoAppLogo from '@renderer/assets/images/apps/duckduckgo.webp?url'
|
||||
import FeloAppLogo from '@renderer/assets/images/apps/felo.png?url'
|
||||
import FlowithAppLogo from '@renderer/assets/images/apps/flowith.svg?url'
|
||||
import GeminiAppLogo from '@renderer/assets/images/apps/gemini.png?url'
|
||||
import GensparkLogo from '@renderer/assets/images/apps/genspark.jpg?url'
|
||||
import GithubCopilotLogo from '@renderer/assets/images/apps/github-copilot.webp?url'
|
||||
import GrokAppLogo from '@renderer/assets/images/apps/grok.png?url'
|
||||
import GrokXAppLogo from '@renderer/assets/images/apps/grok-x.png?url'
|
||||
import HikaLogo from '@renderer/assets/images/apps/hika.webp?url'
|
||||
import HuggingChatLogo from '@renderer/assets/images/apps/huggingchat.svg?url'
|
||||
import KimiAppLogo from '@renderer/assets/images/apps/kimi.webp?url'
|
||||
import LambdaChatLogo from '@renderer/assets/images/apps/lambdachat.webp?url'
|
||||
import LeChatLogo from '@renderer/assets/images/apps/lechat.png?url'
|
||||
import MetasoAppLogo from '@renderer/assets/images/apps/metaso.webp?url'
|
||||
import MonicaLogo from '@renderer/assets/images/apps/monica.webp?url'
|
||||
import NamiAiLogo from '@renderer/assets/images/apps/nm.png?url'
|
||||
import NamiAiSearchLogo from '@renderer/assets/images/apps/nm-search.webp?url'
|
||||
import NotebookLMAppLogo from '@renderer/assets/images/apps/notebooklm.svg?url'
|
||||
import PerplexityAppLogo from '@renderer/assets/images/apps/perplexity.webp?url'
|
||||
import PoeAppLogo from '@renderer/assets/images/apps/poe.webp?url'
|
||||
import ZhipuProviderLogo from '@renderer/assets/images/apps/qingyan.png?url'
|
||||
import QwenlmAppLogo from '@renderer/assets/images/apps/qwenlm.webp?url'
|
||||
import SensetimeAppLogo from '@renderer/assets/images/apps/sensetime.png?url'
|
||||
import SparkDeskAppLogo from '@renderer/assets/images/apps/sparkdesk.webp?url'
|
||||
import ThinkAnyLogo from '@renderer/assets/images/apps/thinkany.webp?url'
|
||||
import TiangongAiLogo from '@renderer/assets/images/apps/tiangong.png?url'
|
||||
import WanZhiAppLogo from '@renderer/assets/images/apps/wanzhi.jpg?url'
|
||||
import WPSLingXiLogo from '@renderer/assets/images/apps/wpslingxi.webp?url'
|
||||
import XiaoYiAppLogo from '@renderer/assets/images/apps/xiaoyi.webp?url'
|
||||
import YouLogo from '@renderer/assets/images/apps/you.jpg?url'
|
||||
import TencentYuanbaoAppLogo from '@renderer/assets/images/apps/yuanbao.webp?url'
|
||||
import YuewenAppLogo from '@renderer/assets/images/apps/yuewen.png?url'
|
||||
import ZaiAppLogo from '@renderer/assets/images/apps/zai.png?url'
|
||||
import ZhihuAppLogo from '@renderer/assets/images/apps/zhihu.png?url'
|
||||
import ClaudeAppLogo from '@renderer/assets/images/models/claude.png?url'
|
||||
import HailuoModelLogo from '@renderer/assets/images/models/hailuo.png?url'
|
||||
import QwenModelLogo from '@renderer/assets/images/models/qwen.png?url'
|
||||
import DeepSeekProviderLogo from '@renderer/assets/images/providers/deepseek.png?url'
|
||||
import GroqProviderLogo from '@renderer/assets/images/providers/groq.png?url'
|
||||
import OpenAiProviderLogo from '@renderer/assets/images/providers/openai.png?url'
|
||||
import SiliconFlowProviderLogo from '@renderer/assets/images/providers/silicon.png?url'
|
||||
|
||||
export default {
|
||||
ThreeMinTopAppLogo,
|
||||
AbacusLogo,
|
||||
AIStudioLogo,
|
||||
BaiduAiAppLogo,
|
||||
BaiduAiSearchLogo,
|
||||
BaicuanAppLogo,
|
||||
BoltAppLogo,
|
||||
CiciAppLogo,
|
||||
CozeAppLogo,
|
||||
DangbeiLogo,
|
||||
DevvAppLogo,
|
||||
DifyAppLogo,
|
||||
DoubaoAppLogo,
|
||||
DuckDuckGoAppLogo,
|
||||
FeloAppLogo,
|
||||
FlowithAppLogo,
|
||||
GeminiAppLogo,
|
||||
GensparkLogo,
|
||||
GithubCopilotLogo,
|
||||
GrokAppLogo,
|
||||
GrokXAppLogo,
|
||||
HikaLogo,
|
||||
HuggingChatLogo,
|
||||
KimiAppLogo,
|
||||
LambdaChatLogo,
|
||||
LeChatLogo,
|
||||
MetasoAppLogo,
|
||||
MonicaLogo,
|
||||
NamiAiLogo,
|
||||
NamiAiSearchLogo,
|
||||
NotebookLMAppLogo,
|
||||
PerplexityAppLogo,
|
||||
PoeAppLogo,
|
||||
QwenlmAppLogo,
|
||||
SensetimeAppLogo,
|
||||
SparkDeskAppLogo,
|
||||
ThinkAnyLogo,
|
||||
TiangongAiLogo,
|
||||
WanZhiAppLogo,
|
||||
WPSLingXiLogo,
|
||||
XiaoYiAppLogo,
|
||||
YouLogo,
|
||||
TencentYuanbaoAppLogo,
|
||||
YuewenAppLogo,
|
||||
ZaiAppLogo,
|
||||
ZhihuAppLogo,
|
||||
ClaudeAppLogo,
|
||||
HailuoModelLogo,
|
||||
QwenModelLogo,
|
||||
DeepSeekProviderLogo,
|
||||
GroqProviderLogo,
|
||||
OpenAiProviderLogo,
|
||||
SiliconFlowProviderLogo,
|
||||
ZhipuProviderLogo
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
import { CherryStoreItem } from '@renderer/types/cherryStore'
|
||||
import { Button } from '@renderer/ui/button'
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@renderer/ui/dropdown-menu'
|
||||
import { Input } from '@renderer/ui/input'
|
||||
import { cn } from '@renderer/utils'
|
||||
import { Filter, Grid3X3, List, Search } from 'lucide-react'
|
||||
import { Grid3X3, List, Search } from 'lucide-react'
|
||||
import React from 'react' // Import React for ComponentType
|
||||
|
||||
// Import the card components
|
||||
// Define the type for a store item based on store_list.json
|
||||
import { GridView } from './GridView'
|
||||
import { ListView } from './ListView'
|
||||
@ -21,6 +22,7 @@ interface StoreContentProps {
|
||||
export function StoreContent({
|
||||
viewMode,
|
||||
searchQuery,
|
||||
selectedCategory, // This prop will drive the component choice
|
||||
items,
|
||||
onSearchQueryChange,
|
||||
onViewModeChange
|
||||
@ -42,19 +44,18 @@ export function StoreContent({
|
||||
onChange={(e) => onSearchQueryChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
{/* <DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Filter className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{/* Add actual filtering logic later */}
|
||||
<DropdownMenuItem>Most Popular</DropdownMenuItem>
|
||||
<DropdownMenuItem>Newest</DropdownMenuItem>
|
||||
<DropdownMenuItem>Highest Rated</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</DropdownMenu> */}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
@ -90,7 +91,7 @@ export function StoreContent({
|
||||
<p className="text-center text-muted-foreground">No items found matching your criteria.</p>
|
||||
</div>
|
||||
) : viewMode === 'grid' ? (
|
||||
<GridView items={items} />
|
||||
<GridView items={items} selectedCategory={selectedCategory} />
|
||||
) : (
|
||||
<ListView items={items} />
|
||||
)}
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
import { Category, SubCategoryItem } from '@renderer/types/cherryStore'
|
||||
import { Badge } from '@renderer/ui/badge'
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@renderer/ui/collapsible'
|
||||
import { Sidebar, SidebarContent, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from '@renderer/ui/sidebar'
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubItem
|
||||
} from '@renderer/ui/sidebar'
|
||||
|
||||
interface StoreSidebarProps {
|
||||
categories: Category[]
|
||||
selectedCategory: string
|
||||
selectedSubcategory: string
|
||||
onSelectCategory: (categoryId: string, subcategoryId: string, row: SubCategoryItem) => void
|
||||
onSelectCategory: (categoryId: string, subcategoryId: string, row?: SubCategoryItem) => void
|
||||
}
|
||||
|
||||
export function StoreSidebar({
|
||||
@ -30,44 +38,62 @@ export function StoreSidebar({
|
||||
<Sidebar className="absolute top-0 left-0 h-full border-r">
|
||||
<SidebarContent>
|
||||
<SidebarMenu className="gap-0">
|
||||
{categories.map((category, index) => (
|
||||
<Collapsible key={category.id} defaultOpen={index === 0} className="group/collapsible w-full">
|
||||
<SidebarMenuItem className="w-full px-0 py-0">
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
variant="outline"
|
||||
className="rounded-none hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none">
|
||||
<span className="truncate">{category.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="overflow-hidden text-sm transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
|
||||
<SidebarMenu className="py-1 pr-1 pl-4">
|
||||
{category.items.map((subItem) => (
|
||||
<SidebarMenuItem key={subItem.id}>
|
||||
<SidebarMenuButton
|
||||
isActive={category.id === selectedCategory && subItem.id === selectedSubcategory}
|
||||
className="w-full justify-between"
|
||||
onClick={() => {
|
||||
onSelectCategory(category.id, subItem.id, subItem)
|
||||
}}
|
||||
size="sm">
|
||||
<span className="truncate">{subItem.name}</span>
|
||||
{typeof subItem.count === 'number' && (
|
||||
<Badge variant="secondary" className="ml-auto shrink-0">
|
||||
{subItem.count}
|
||||
</Badge>
|
||||
)}
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
{category.items.length === 0 && (
|
||||
<SidebarMenuItem className="px-3 py-1.5 text-xs text-muted-foreground">No items</SidebarMenuItem>
|
||||
)}
|
||||
</SidebarMenu>
|
||||
</CollapsibleContent>
|
||||
{categories.map((category, index) =>
|
||||
category.items?.length ? (
|
||||
<Collapsible key={category.id} defaultOpen={index === 0} className="group/collapsible w-full">
|
||||
<SidebarMenuItem className="w-full px-0 py-0">
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
variant="outline"
|
||||
className="rounded-none hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none">
|
||||
<span className="truncate">{category.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="overflow-hidden text-sm transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
|
||||
<SidebarMenuSub className="py-1 pr-1 pl-4">
|
||||
{category.items.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.id}>
|
||||
<SidebarMenuButton
|
||||
isActive={category.id === selectedCategory && subItem.id === selectedSubcategory}
|
||||
className="w-full justify-between"
|
||||
onClick={() => {
|
||||
onSelectCategory(category.id, subItem.id, subItem)
|
||||
}}
|
||||
size="sm">
|
||||
<span className="truncate">{subItem.name}</span>
|
||||
{typeof subItem.count === 'number' && (
|
||||
<Badge variant="secondary" className="ml-auto shrink-0">
|
||||
{subItem.count}
|
||||
</Badge>
|
||||
)}
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
{/* {category.items.length === 0 && (
|
||||
<SidebarMenuSubItem className="px-3 py-1.5 text-xs text-muted-foreground">
|
||||
No items
|
||||
</SidebarMenuSubItem>
|
||||
)} */}
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
) : (
|
||||
<SidebarMenuItem key={category.id}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={category.id === selectedCategory}
|
||||
variant="outline"
|
||||
className="rounded-none hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none"
|
||||
onClick={() => {
|
||||
onSelectCategory(category.id, '')
|
||||
}}>
|
||||
{/* <item.icon /> */}
|
||||
<span>{category.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</SidebarMenu>
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
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
|
||||
}
|
||||
@ -8,7 +8,7 @@ import { Download } from 'lucide-react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export function ItemDetailDialog({
|
||||
export default function InstallDialog({
|
||||
item,
|
||||
isOpen,
|
||||
onClose
|
||||
36
src/renderer/src/pages/store/components/dialog/index.tsx
Normal file
36
src/renderer/src/pages/store/components/dialog/index.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,13 @@ import { SidebarProvider } from '@renderer/ui/sidebar'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
// Import Context and the main Dialog Manager component
|
||||
import { DialogManagerProvider } from './components/dialog/DialogManagerContext'
|
||||
import Dialogs from './components/dialog/index'
|
||||
import { StoreContent } from './components/StoreContent'
|
||||
import { StoreSidebar } from './components/StoreSidebar'
|
||||
import { loadAndFilterItems, loadCategories } from './data'
|
||||
|
||||
export default function StoreLayout() {
|
||||
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
@ -67,10 +71,10 @@ export default function StoreLayout() {
|
||||
fetchItems()
|
||||
}, [selectedCategory, selectedSubcategory, searchQuery])
|
||||
|
||||
const handleSelectCategory = (categoryId: string, subcategoryId: string, row: SubCategoryItem) => {
|
||||
const handleSelectCategory = (categoryId: string, subcategoryId: string, row?: SubCategoryItem) => {
|
||||
setSelectedCategory(categoryId)
|
||||
setSelectedSubcategory(subcategoryId)
|
||||
setSearchQuery(row.name)
|
||||
setSearchQuery(row?.name || '')
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
@ -80,34 +84,38 @@ export default function StoreLayout() {
|
||||
if (error) {
|
||||
return <div className="p-4 text-center text-red-500">Error: {error}</div>
|
||||
}
|
||||
|
||||
console.log('categories', categories)
|
||||
return (
|
||||
<div className="h-[calc(100vh_-_var(--navbar-height))] w-full">
|
||||
<Navbar className="h-full">
|
||||
<NavbarCenter>{t('store.title')}</NavbarCenter>
|
||||
</Navbar>
|
||||
<div id="content-container" className="h-full w-full">
|
||||
<SidebarProvider className="relative h-full min-h-full w-full">
|
||||
<StoreSidebar
|
||||
categories={categories}
|
||||
selectedCategory={selectedCategory}
|
||||
selectedSubcategory={selectedSubcategory}
|
||||
onSelectCategory={handleSelectCategory}
|
||||
/>
|
||||
{isLoadingItems ? (
|
||||
<div className="p-4 text-center">Loading items...</div>
|
||||
) : (
|
||||
<StoreContent
|
||||
viewMode={viewMode}
|
||||
searchQuery={searchQuery}
|
||||
<DialogManagerProvider>
|
||||
<div className="h-[calc(100vh_-_var(--navbar-height))] w-full">
|
||||
<Navbar className="h-full">
|
||||
<NavbarCenter>{t('store.title')}</NavbarCenter>
|
||||
</Navbar>
|
||||
<div id="content-container" className="h-full w-full">
|
||||
<SidebarProvider className="relative h-full min-h-full w-full">
|
||||
<StoreSidebar
|
||||
categories={categories}
|
||||
selectedCategory={selectedCategory}
|
||||
items={items}
|
||||
onSearchQueryChange={setSearchQuery}
|
||||
onViewModeChange={setViewMode}
|
||||
selectedSubcategory={selectedSubcategory}
|
||||
onSelectCategory={handleSelectCategory}
|
||||
/>
|
||||
)}
|
||||
</SidebarProvider>
|
||||
{isLoadingItems ? (
|
||||
// TODO: 添加 loading 动画
|
||||
<div className="p-4 text-center">Loading items...</div>
|
||||
) : (
|
||||
<StoreContent
|
||||
viewMode={viewMode}
|
||||
searchQuery={searchQuery}
|
||||
selectedCategory={selectedCategory}
|
||||
items={items}
|
||||
onSearchQueryChange={setSearchQuery}
|
||||
onViewModeChange={setViewMode}
|
||||
/>
|
||||
)}
|
||||
</SidebarProvider>
|
||||
</div>
|
||||
<Dialogs />
|
||||
</div>
|
||||
</div>
|
||||
</DialogManagerProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ export interface SubCategoryItem {
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
id: string
|
||||
id: CherryStoreType
|
||||
title: string
|
||||
items: SubCategoryItem[]
|
||||
}
|
||||
@ -40,4 +40,13 @@ export interface AssistantItem extends CherryStoreBaseItem {
|
||||
prompt?: string
|
||||
}
|
||||
|
||||
export type CherryStoreItem = AssistantItem
|
||||
export interface MiniAppItem extends CherryStoreBaseItem {
|
||||
type: CherryStoreType.MINI_APP
|
||||
url: string
|
||||
bodered?: boolean
|
||||
style?: {
|
||||
padding?: number
|
||||
}
|
||||
}
|
||||
|
||||
export type CherryStoreItem = AssistantItem | MiniAppItem
|
||||
|
||||
42
yarn.lock
42
yarn.lock
@ -5338,7 +5338,7 @@ __metadata:
|
||||
lucide-react: "npm:^0.509.0"
|
||||
markdown-it: "npm:^14.1.0"
|
||||
mime: "npm:^4.0.4"
|
||||
motion: "npm:^12.11.0"
|
||||
motion: "npm:^12.12.1"
|
||||
next-themes: "npm:^0.4.6"
|
||||
node-stream-zip: "npm:^1.15.0"
|
||||
npx-scope-finder: "npm:^1.2.0"
|
||||
@ -9464,12 +9464,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"framer-motion@npm:^12.11.0":
|
||||
version: 12.11.0
|
||||
resolution: "framer-motion@npm:12.11.0"
|
||||
"framer-motion@npm:^12.12.1":
|
||||
version: 12.12.1
|
||||
resolution: "framer-motion@npm:12.12.1"
|
||||
dependencies:
|
||||
motion-dom: "npm:^12.11.0"
|
||||
motion-utils: "npm:^12.9.4"
|
||||
motion-dom: "npm:^12.12.1"
|
||||
motion-utils: "npm:^12.12.1"
|
||||
tslib: "npm:^2.4.0"
|
||||
peerDependencies:
|
||||
"@emotion/is-prop-valid": "*"
|
||||
@ -9482,7 +9482,7 @@ __metadata:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
checksum: 10c0/369026997d12e51ba5ca12ecf507ccf2108941c9e634d0d57ff93524d56d72dc38cd909ce1272a155d15b92faf0ddf9268942908be29032e36cc535a353fd497
|
||||
checksum: 10c0/a782ffa3613f35d23b550123e5409504dadd1f708af40d6014969a8c78dae31046ab9c35a44cbb768e20cec837675d1e240e06e01818b250386af6d0ff570593
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -13460,27 +13460,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"motion-dom@npm:^12.11.0":
|
||||
version: 12.11.0
|
||||
resolution: "motion-dom@npm:12.11.0"
|
||||
"motion-dom@npm:^12.12.1":
|
||||
version: 12.12.1
|
||||
resolution: "motion-dom@npm:12.12.1"
|
||||
dependencies:
|
||||
motion-utils: "npm:^12.9.4"
|
||||
checksum: 10c0/9fd7441c38b28560ea2db0f4dbd6f412873e777f5d32e623792cc8ff32c0bbff761f68102115060af81325227cc639e548f6123bdced50722f55bc2abda76b55
|
||||
motion-utils: "npm:^12.12.1"
|
||||
checksum: 10c0/d80cdde894d07042e792ae69f47d7b58aa20691d4e4da7ce803af246295855fd59fdbd7a9e0def8554e22fd105c407cc672bd85e0a79a660d523adee7989d61d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"motion-utils@npm:^12.9.4":
|
||||
version: 12.9.4
|
||||
resolution: "motion-utils@npm:12.9.4"
|
||||
checksum: 10c0/b6783babfd1282ad320585f7cdac9fe7a1f97b39e07d12a500d3709534441bd9d49b556fa1cd838d1bde188570d4ab6b4c5aa9d297f7f5aa9dc16d600c17afdc
|
||||
"motion-utils@npm:^12.12.1":
|
||||
version: 12.12.1
|
||||
resolution: "motion-utils@npm:12.12.1"
|
||||
checksum: 10c0/880a174769d1be42b46cfb34af81b4a629c068d30d5cf7e07d249fbf2f5121d577482d3ea5bdc1db549c0288733e1e987efecb195fae350995270651559c6697
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"motion@npm:^12.11.0":
|
||||
version: 12.11.0
|
||||
resolution: "motion@npm:12.11.0"
|
||||
"motion@npm:^12.12.1":
|
||||
version: 12.12.1
|
||||
resolution: "motion@npm:12.12.1"
|
||||
dependencies:
|
||||
framer-motion: "npm:^12.11.0"
|
||||
framer-motion: "npm:^12.12.1"
|
||||
tslib: "npm:^2.4.0"
|
||||
peerDependencies:
|
||||
"@emotion/is-prop-valid": "*"
|
||||
@ -13493,7 +13493,7 @@ __metadata:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
checksum: 10c0/1066de76a5d8a7a6898d8b47afe4363098466c67ed79cb989c0f05c11aa62a2cc83f61cc283ec07b452ae0622f6cd1c4584f1a0a68174798d67ba8811c52ac11
|
||||
checksum: 10c0/1083c6138486c71d946379bb0d7559e11ab27f6c4ed2aed1d874c0b00fc762d5064bcf98079a313fcb8fc923c2457213d35fa7ca7e2a2bd53e71bef57cb94948
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user