feat: add cherry-text-logo.svg and remove npm.svg; update MCPSettings and NpxSearch components

- Introduced a new cherry-text-logo.svg file for branding.
- Removed the deprecated npm.svg file.
- Refactored MCPSettings and NpxSearch components to enhance functionality and UI, including state management and layout adjustments.
- Updated translations in multiple locales to include new types for MCP servers.
This commit is contained in:
kangfenmao 2025-04-24 15:17:15 +08:00
parent a33b0a0c55
commit 7311af9a56
14 changed files with 224 additions and 189 deletions

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.45 66.73">
<defs>
<style>
.cls-1 {
fill: #ea5e5d;
}
.cls-2 {
fill: #23af69;
}
.cls-3 {
fill: #ea5756;
}
</style>
</defs>
<g id="_图层_1-2" data-name="图层_1">
<g>
<g>
<g>
<path class="cls-1" d="M16.72,51.21c-4.45,0-8.64-1.78-11.81-5.01-3.17-3.23-4.91-7.51-4.91-12.04s1.74-8.81,4.91-12.04,7.36-5.01,11.81-5.01,8.71,1.82,11.82,4.99c2.32,2.36,2.32,6.2,0,8.56-2.32,2.36-6.08,2.36-8.4,0-.9-.92-2.15-1.45-3.43-1.45-2.63,0-4.85,2.26-4.85,4.94s2.22,4.94,4.85,4.94c1.28,0,2.52-.53,3.43-1.45,2.32-2.36,6.08-2.36,8.4,0,2.32,2.36,2.32,6.2,0,8.56-3.11,3.17-7.42,4.99-11.82,4.99Z"/>
<path class="cls-1" d="M32.05,66.73c-4.45,0-8.64-1.78-11.81-5.01s-4.91-7.51-4.91-12.04,1.79-8.88,4.9-12.06c2.32-2.36,6.08-2.36,8.4,0,2.32,2.36,2.32,6.2,0,8.56-.9.92-1.42,2.19-1.42,3.49,0,2.68,2.22,4.94,4.85,4.94s4.85-2.26,4.85-4.94c0-.95-.23-2.31-1.32-3.43-3.13-3.19-4.92-7.6-4.92-12.09s1.74-8.81,4.91-12.04,7.36-5.01,11.81-5.01,8.64,1.78,11.81,5.01,4.91,7.51,4.91,12.04-1.79,8.88-4.9,12.06c-2.32,2.36-6.08,2.36-8.4,0-2.32-2.36-2.32-6.2,0-8.56.9-.92,1.42-2.19,1.42-3.49,0-2.68-2.22-4.94-4.85-4.94s-4.85,2.26-4.85,4.94c0,1.31.53,2.6,1.45,3.53,3.1,3.16,4.8,7.42,4.8,11.99s-1.74,8.81-4.91,12.04c-3.17,3.23-7.36,5.01-11.81,5.01Z"/>
</g>
<path class="cls-2" d="M32.05,19.09l-9.72-9.12c-1.5-1.4-1.57-3.75-.17-5.25,1.4-1.49,3.75-1.57,5.25-.17l3.89,3.65,5.53-6.83c1.29-1.59,3.63-1.84,5.22-.55,1.59,1.29,1.84,3.63.55,5.22l-10.56,13.05Z"/>
</g>
<g>
<path class="cls-3" d="M93.93,24.6l.55-.39c.69-.4,1.17-.61,1.46-.61.63,0,1.3.57,2.03,1.7.44.71.67,1.27.67,1.7s-.14.78-.41,1.06c-.27.28-.59.54-.96.76-.36.22-.71.43-1.05.64-.33.2-1.02.47-2.05.79-1.03.32-2.03.49-2.99.49s-1.93-.13-2.91-.38c-.98-.25-1.99-.68-3.03-1.27-1.04-.6-1.98-1.32-2.81-2.18-.83-.86-1.51-1.96-2.05-3.31-.54-1.35-.8-2.81-.8-4.38s.26-3.01.79-4.29c.53-1.28,1.2-2.35,2.02-3.19.82-.84,1.75-1.54,2.81-2.11,1.98-1.09,3.97-1.64,5.98-1.64.95,0,1.92.15,2.9.44.98.29,1.72.59,2.23.9l.73.42c.36.22.65.4.85.55.53.42.79.91.79,1.44s-.21,1.1-.64,1.68c-.79,1.09-1.5,1.64-2.12,1.64-.36,0-.88-.22-1.55-.67-.85-.69-1.98-1.03-3.4-1.03-1.31,0-2.61.46-3.88,1.36-.61.44-1.11,1.07-1.52,1.88-.4.81-.61,1.72-.61,2.75s.2,1.94.61,2.75c.4.81.92,1.45,1.55,1.91,1.23.89,2.52,1.34,3.85,1.34.63,0,1.22-.08,1.77-.24.56-.16.96-.32,1.2-.49Z"/>
<path class="cls-3" d="M114.38,9.07c.16-.3.43-.52.82-.64.38-.12.87-.18,1.46-.18s1.05.05,1.4.15c.34.1.61.22.79.36.18.14.32.34.42.61.1.34.15.87.15,1.58v16.84c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.28.55-1.07.82-2.37.82-1.42,0-2.25-.37-2.49-1.12-.12-.34-.18-.87-.18-1.58v-6.16h-8.04v6.19c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.28.55-1.07.82-2.37.82-1.42,0-2.25-.37-2.49-1.12-.12-.34-.18-.87-.18-1.58V10.92c0-.46.02-.81.05-1.05.03-.23.13-.5.29-.8.28-.55,1.07-.82,2.37-.82,1.42,0,2.25.37,2.52,1.12.1.34.15.87.15,1.58v6.19h8.04v-6.22c0-.46.02-.81.05-1.05.03-.23.13-.5.29-.8Z"/>
<path class="cls-3" d="M127.21,25.1h9.34c.47,0,.81.02,1.05.05.23.03.5.13.8.29.55.28.82,1.07.82,2.37,0,1.42-.37,2.25-1.12,2.49-.34.12-.87.18-1.58.18h-12.01c-1.42,0-2.25-.38-2.49-1.15-.12-.32-.18-.84-.18-1.55V10.9c0-1.03.19-1.73.58-2.11.38-.37,1.11-.56,2.18-.56h11.95c.47,0,.81.02,1.05.05.23.03.5.13.8.29.55.28.82,1.07.82,2.37,0,1.42-.37,2.25-1.12,2.49-.34.12-.87.18-1.58.18h-9.31v3.06h6.01c.46,0,.81.02,1.05.05.23.03.5.13.8.29.55.28.82,1.07.82,2.37,0,1.42-.38,2.25-1.15,2.49-.34.12-.87.18-1.58.18h-5.95v3.06Z"/>
<path class="cls-3" d="M196.96,8.79c.99.69,1.49,1.35,1.49,2,0,.38-.23.92-.7,1.61l-6.55,9.8v5.79c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.16.3-.43.52-.82.64-.38.12-.9.18-1.55.18s-1.16-.06-1.55-.18c-.38-.12-.66-.34-.82-.65-.16-.31-.26-.59-.29-.82-.03-.23-.05-.59-.05-1.08v-5.73l-6.55-9.8c-.47-.69-.7-1.22-.7-1.61,0-.65.44-1.27,1.33-1.87.89-.6,1.53-.9,1.91-.9s.69.08.91.24c.34.22.71.64,1.09,1.24l4.7,7.52,4.7-7.52c.38-.61.72-1.01,1-1.2s.61-.29.99-.29.97.25,1.77.76Z"/>
<g>
<path class="cls-3" d="M81.93,56.63c-.53-.65-.79-1.23-.79-1.74s.43-1.2,1.3-2.05c.51-.49,1.04-.73,1.61-.73s1.36.51,2.37,1.52c.28.34.69.67,1.21.99.53.31,1.01.47,1.46.47,1.88,0,2.82-.77,2.82-2.31,0-.46-.26-.85-.77-1.17-.52-.31-1.16-.54-1.93-.68-.77-.14-1.6-.37-2.49-.68-.89-.31-1.72-.68-2.49-1.11-.77-.42-1.41-1.1-1.93-2.02-.52-.92-.77-2.03-.77-3.32,0-1.78.66-3.33,1.99-4.66s3.13-1.99,5.42-1.99c1.21,0,2.32.16,3.32.47,1,.31,1.69.63,2.08.96l.76.58c.63.59.94,1.08.94,1.49s-.24.96-.73,1.67c-.69,1.01-1.4,1.52-2.12,1.52-.42,0-.95-.2-1.58-.61-.06-.04-.18-.14-.35-.3-.17-.16-.33-.29-.47-.39-.42-.26-.97-.39-1.62-.39s-1.2.16-1.64.47c-.43.31-.65.75-.65,1.3s.26,1.01.77,1.35c.52.34,1.16.58,1.93.7.77.12,1.61.31,2.52.56.91.25,1.75.56,2.52.93.77.36,1.41,1,1.93,1.9.52.9.77,2.01.77,3.32s-.26,2.47-.79,3.47c-.53,1-1.21,1.77-2.06,2.32-1.64,1.07-3.39,1.61-5.25,1.61-.95,0-1.85-.12-2.7-.35-.85-.23-1.54-.52-2.06-.86-1.07-.65-1.82-1.27-2.24-1.88l-.27-.33Z"/>
<path class="cls-3" d="M100.74,37.49h16.87c.65,0,1.12.08,1.43.23.3.15.51.39.61.71.1.32.15.75.15,1.27s-.05.95-.15,1.26c-.1.31-.27.53-.52.65-.36.18-.88.27-1.55.27h-5.79v15.26c0,.47-.02.81-.05,1.03s-.12.48-.27.77c-.15.29-.42.5-.8.62-.38.12-.89.18-1.52.18s-1.13-.06-1.5-.18c-.37-.12-.64-.33-.79-.62-.15-.29-.24-.56-.27-.79-.03-.23-.05-.58-.05-1.05v-15.23h-5.82c-.65,0-1.12-.08-1.43-.23-.3-.15-.51-.39-.61-.71-.1-.32-.15-.75-.15-1.27s.05-.95.15-1.26c.1-.31.27-.53.52-.65.36-.18.88-.27,1.55-.27Z"/>
<path class="cls-3" d="M135.99,38.34c.2-.32.5-.55.88-.67.38-.12.86-.18,1.44-.18s1.04.05,1.38.15c.34.1.61.22.79.36.18.14.31.35.39.64.12.34.18.87.18,1.58v9.16c0,2.67-.83,5.1-2.49,7.28-.81,1.03-1.85,1.87-3.12,2.5s-2.68.96-4.23.96-2.95-.32-4.22-.97c-1.26-.65-2.29-1.5-3.08-2.55-1.64-2.14-2.46-4.57-2.46-7.28v-9.13c0-.49.02-.84.05-1.08.03-.23.13-.5.29-.8.16-.3.43-.52.82-.64.38-.12.9-.18,1.55-.18s1.16.06,1.55.18c.38.12.65.33.79.64.24.47.36,1.1.36,1.91v9.1c0,1.23.3,2.41.91,3.52.3.57.76,1.02,1.37,1.36.61.34,1.32.52,2.15.52,1.48,0,2.58-.55,3.31-1.64.73-1.09,1.09-2.36,1.09-3.79v-9.28c0-.79.1-1.34.3-1.67Z"/>
<path class="cls-3" d="M146.18,37.49l5.61.03c2.93,0,5.51,1.06,7.74,3.17,2.22,2.11,3.34,4.71,3.34,7.8s-1.09,5.73-3.26,7.93c-2.17,2.2-4.81,3.31-7.9,3.31h-5.55c-1.23,0-2-.25-2.31-.76-.24-.42-.36-1.07-.36-1.94v-16.87c0-.49.02-.84.05-1.06s.13-.49.29-.79c.28-.55,1.07-.82,2.37-.82ZM151.79,54.35c1.46,0,2.77-.54,3.94-1.62,1.17-1.08,1.76-2.44,1.76-4.08s-.57-3.01-1.71-4.11c-1.14-1.1-2.48-1.65-4.02-1.65h-2.91v11.47h2.94Z"/>
<path class="cls-3" d="M164.84,40.19c0-.46.02-.81.05-1.05.03-.23.13-.5.29-.8.28-.55,1.07-.82,2.37-.82,1.42,0,2.25.37,2.52,1.12.1.34.15.87.15,1.58v16.87c0,.49-.02.84-.05,1.06s-.13.49-.29.79c-.28.55-1.07.82-2.37.82-1.42,0-2.25-.38-2.49-1.15-.12-.32-.18-.84-.18-1.55v-16.87Z"/>
<path class="cls-3" d="M183.07,37.24c2.99,0,5.59,1.08,7.8,3.25,2.2,2.16,3.31,4.85,3.31,8.05s-1.05,5.94-3.16,8.19c-2.1,2.26-4.69,3.38-7.77,3.38s-5.69-1.11-7.84-3.34c-2.15-2.22-3.23-4.87-3.23-7.95,0-1.68.3-3.25.91-4.72.61-1.47,1.42-2.7,2.43-3.69,1.01-.99,2.17-1.77,3.49-2.34,1.31-.57,2.67-.85,4.07-.85ZM177.55,48.68c0,1.8.58,3.26,1.74,4.38,1.16,1.12,2.46,1.68,3.9,1.68s2.73-.55,3.88-1.64c1.15-1.09,1.73-2.56,1.73-4.4s-.58-3.32-1.74-4.43c-1.16-1.11-2.46-1.67-3.9-1.67s-2.73.56-3.88,1.68c-1.15,1.12-1.73,2.58-1.73,4.38Z"/>
</g>
<g>
<path class="cls-3" d="M176.92,11.06c-.03-.23-.13-.5-.29-.8-.28-.55-1.07-.82-2.37-.82h-6.55c-1.78,0-3.51.65-5.19,1.94-.81.63-1.48,1.48-2,2.55-.53,1.07-.79,2.27-.79,3.58,0,2.29.76,4.17,2.28,5.64-.44,1.07-1.13,2.66-2.06,4.76-.3.73-.45,1.25-.45,1.58,0,.77.63,1.42,1.88,1.94.65.28,1.17.43,1.56.43s.72-.1.97-.29c.25-.19.44-.39.56-.59.2-.38.99-2.21,2.37-5.49l.94.06h3.82v3.43c0,.47.02.81.05,1.05.03.23.13.5.29.8.28.55,1.07.82,2.37.82,1.42,0,2.25-.37,2.49-1.12.12-.34.18-.87.18-1.58V12.11c0-.46-.02-.81-.05-1.05ZM172.81,19.44c-.09.14-.48.77-1.24.91-.2.04-.37.03-.48.02-.02.14-.04.26-.06.38-.16.83-.38,1.05-.57,1.07-.29.05-.51-.35-.93-.9-.23.01-.46.02-.69.02-.51,0-1.01-.03-1.49-.09-.25-.03-.5-.07-.74-.11-1.18-.32-2.03-1.27-2.03-2.4v-1.37c0-1.13.86-2.08,2.03-2.4.24-.04.49-.08.74-.11.48-.06.98-.09,1.49-.09s1.01.03,1.49.09c.25.03.5.07.74.11.6.16,1.12.49,1.49.93.34.41.55.92.55,1.47v1.37c0,.23-.01.66-.29,1.1Z"/>
<circle class="cls-2" cx="167.24" cy="17.67" r=".49"/>
<circle class="cls-2" cx="168.88" cy="17.71" r=".49"/>
<circle class="cls-2" cx="170.59" cy="17.71" r=".49"/>
</g>
<g>
<path class="cls-3" d="M141.01,8.24c.03-.23.13-.5.29-.8.28-.55,1.07-.82,2.37-.82h6.55c1.78,0,3.51.65,5.19,1.94.81.63,1.48,1.48,2,2.55.53,1.07.79,2.27.79,3.58,0,2.29-.76,4.17-2.28,5.64.44,1.07,1.13,2.66,2.06,4.76.3.73.45,1.25.45,1.58,0,.77-.63,1.42-1.88,1.94-.65.28-1.17.43-1.56.43s-.72-.1-.97-.29c-.25-.19-.44-.39-.56-.59-.2-.38-.99-2.21-2.37-5.49l-.94.06h-3.82v3.43c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.28.55-1.07.82-2.37.82-1.42,0-2.25-.37-2.49-1.12-.12-.34-.18-.87-.18-1.58V9.28c0-.46.02-.81.05-1.05ZM145.12,16.62c.09.14.48.77,1.24.91.2.04.37.03.48.02.02.14.04.26.06.38.16.83.38,1.05.57,1.07.29.05.51-.35.93-.9.23.01.46.02.69.02.51,0,1.01-.03,1.49-.09.25-.03.5-.07.74-.11,1.18-.32,2.03-1.27,2.03-2.4v-1.37c0-1.13-.86-2.08-2.03-2.4-.24-.04-.49-.08-.74-.11-.48-.06-.98-.09-1.49-.09s-1.01.03-1.49.09c-.25.03-.5.07-.74.11-.6.16-1.12.49-1.49.93-.34.41-.55.92-.55,1.47v1.37c0,.23.01.66.29,1.1Z"/>
<circle class="cls-2" cx="150.69" cy="14.84" r=".49"/>
<circle class="cls-2" cx="149.05" cy="14.89" r=".49"/>
<circle class="cls-2" cx="147.35" cy="14.89" r=".49"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1744456106953" class="icon" viewBox="0 0 2633 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1486" xmlns:xlink="http://www.w3.org/1999/xlink" width="514.2578125" height="200"><path d="M0 0v877.843607h731.440328v146.156393H1316.724263v-146.156393h1316.724262V0z m731.440328 731.196014h-146.156393V292.312786h-146.485574v438.883228H146.485574V146.238688h584.954754z m438.880656 0v146.567869h-292.405369V146.238688H1463.209837v585.037049H1170.320984z m1316.888853 0H2341.30033V292.312786h-146.56787v438.883228h-146.485574V292.312786h-145.909508v438.883228H1609.283935V146.238688h878.008197zM1170.238688 292.477377H1316.724263v292.644539h-146.485575z" fill="#CB3837" p-id="1487"></path></svg>

Before

Width:  |  Height:  |  Size: 845 B

View File

@ -29,8 +29,13 @@ export const useMCPServers = () => {
}
export const useMCPServer = (id: string) => {
const { mcpServers } = useMCPServers()
const server = useAppSelector((state) => (state.mcp.servers || []).find((server) => server.id === id))
const dispatch = useAppDispatch()
return {
server: mcpServers.find((server) => server.id === id)
server,
updateMCPServer: (server: MCPServer) => dispatch(updateMCPServer(server)),
setMCPServerActive: (server: MCPServer, isActive: boolean) => dispatch(updateMCPServer({ ...server, isActive })),
deleteMCPServer: (id: string) => dispatch(deleteMCPServer(id))
}
}

View File

@ -1156,7 +1156,13 @@
"registryDefault": "Default",
"not_support": "Model not supported",
"user": "User",
"system": "System"
"system": "System",
"types": {
"inMemory": "In Memory",
"sse": "SSE",
"streamableHttp": "Streamable HTTP",
"stdio": "STDIO"
}
},
"messages.divider": "Show divider between messages",
"messages.grid_columns": "Message grid display columns",
@ -1442,4 +1448,4 @@
"visualization": "Visualization"
}
}
}
}

View File

@ -1155,7 +1155,13 @@
"registryDefault": "デフォルト",
"not_support": "モデルはサポートされていません",
"user": "ユーザー",
"system": "システム"
"system": "システム",
"types": {
"inMemory": "組み込み",
"sse": "SSE",
"streamableHttp": "ストリーミング",
"stdio": "STDIO"
}
},
"messages.divider": "メッセージ間に区切り線を表示",
"messages.grid_columns": "メッセージグリッドの表示列数",
@ -1442,4 +1448,4 @@
"visualization": "可視化"
}
}
}
}

View File

@ -1155,7 +1155,13 @@
"registryDefault": "По умолчанию",
"not_support": "Модель не поддерживается",
"user": "Пользователь",
"system": "Система"
"system": "Система",
"types": {
"inMemory": "Встроенный",
"sse": "SSE",
"streamableHttp": "Потоковый HTTP",
"stdio": "STDIO"
}
},
"messages.divider": "Показывать разделитель между сообщениями",
"messages.grid_columns": "Количество столбцов сетки сообщений",
@ -1442,4 +1448,4 @@
"visualization": "Визуализация"
}
}
}
}

View File

@ -1156,7 +1156,13 @@
"registryDefault": "默认",
"not_support": "模型不支持",
"user": "用户",
"system": "系统"
"system": "系统",
"types": {
"inMemory": "内置",
"sse": "SSE",
"streamableHttp": "流式",
"stdio": "STDIO"
}
},
"messages.divider": "消息分割线",
"messages.grid_columns": "消息网格展示列数",
@ -1442,4 +1448,4 @@
"visualization": "可视化"
}
}
}
}

View File

@ -1155,7 +1155,13 @@
"registryDefault": "預設",
"not_support": "不支援此模型",
"user": "用戶",
"system": "系統"
"system": "系統",
"types": {
"inMemory": "內置",
"sse": "SSE",
"streamableHttp": "流式",
"stdio": "STDIO"
}
},
"messages.divider": "訊息間顯示分隔線",
"messages.grid_columns": "訊息網格展示列數",
@ -1442,4 +1448,4 @@
"visualization": "視覺化"
}
}
}
}

View File

@ -99,6 +99,7 @@ const InstallNpxUv: FC<Props> = ({ mini = false }) => {
<Alert
type={isUvInstalled ? 'success' : 'warning'}
banner
style={{ borderRadius: 'var(--list-item-border-radius)' }}
description={
<VStack>
<SettingRow style={{ width: '100%' }}>
@ -129,6 +130,7 @@ const InstallNpxUv: FC<Props> = ({ mini = false }) => {
<Alert
type={isBunInstalled ? 'success' : 'warning'}
banner
style={{ borderRadius: 'var(--list-item-border-radius)' }}
description={
<VStack>
<SettingRow style={{ width: '100%' }}>
@ -170,6 +172,7 @@ const Container = styled.div`
flex-direction: column;
margin-bottom: 20px;
gap: 12px;
padding-top: 50px;
`
export default InstallNpxUv

View File

@ -1,26 +1,22 @@
import { CodeOutlined, PlusOutlined } from '@ant-design/icons'
import { EditOutlined } from '@ant-design/icons'
import { nanoid } from '@reduxjs/toolkit'
import DragableList from '@renderer/components/DragableList'
import IndicatorLight from '@renderer/components/IndicatorLight'
import { HStack, VStack } from '@renderer/components/Layout'
import Scrollbar from '@renderer/components/Scrollbar'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { MCPServer } from '@renderer/types'
import { Button, Empty, Tag } from 'antd'
import { MonitorCheck, Plus, Settings2 } from 'lucide-react'
import { FC, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
import styled from 'styled-components'
import { SettingTitle } from '..'
import McpSettings from './McpSettings'
interface Props {
selectedMcpServer: MCPServer | null
setSelectedMcpServer: (server: MCPServer | null) => void
}
const McpServersList: FC<Props> = ({ selectedMcpServer, setSelectedMcpServer }) => {
import EditMcpJsonPopup from './EditMcpJsonPopup'
const McpServersList: FC = () => {
const { mcpServers, addMCPServer, updateMcpServers } = useMCPServers()
const { t } = useTranslation()
const navigate = useNavigate()
const onAddMcpServer = useCallback(async () => {
const newServer = {
@ -33,78 +29,81 @@ const McpServersList: FC<Props> = ({ selectedMcpServer, setSelectedMcpServer })
env: {},
isActive: false
}
addMCPServer(newServer)
await addMCPServer(newServer)
navigate(`/settings/mcp/settings`, { state: { server: newServer } })
window.message.success({ content: t('settings.mcp.addSuccess'), key: 'mcp-list' })
setSelectedMcpServer(newServer)
}, [addMCPServer, setSelectedMcpServer, t])
}, [addMCPServer, navigate, t])
return (
<Container>
<ServersList>
<ListHeader>
<SettingTitle>{t('settings.mcp.newServer')}</SettingTitle>
</ListHeader>
<AddServerCard onClick={onAddMcpServer}>
<PlusOutlined style={{ fontSize: 24 }} />
<AddServerText>{t('settings.mcp.addServer')}</AddServerText>
</AddServerCard>
<DragableList list={mcpServers} onUpdate={updateMcpServers}>
{(server) => (
<ServerCard
key={server.id}
onClick={() => setSelectedMcpServer(server)}
className={selectedMcpServer?.id === server.id ? 'active' : ''}>
<ServerHeader>
<ListHeader>
<SettingTitle style={{ gap: 3 }}>
<span>{t('settings.mcp.newServer')}</span>
<Button icon={<EditOutlined />} type="text" onClick={() => EditMcpJsonPopup.show()} shape="circle" />
</SettingTitle>
<Button icon={<Plus size={16} />} type="default" onClick={onAddMcpServer} shape="round">
{t('settings.mcp.addServer')}
</Button>
</ListHeader>
<DragableList style={{ width: '100%' }} list={mcpServers} onUpdate={updateMcpServers}>
{(server: MCPServer) => (
<ServerCard key={server.id} onClick={() => navigate(`/settings/mcp/settings`, { state: { server } })}>
<ServerHeader>
<ServerName>
<ServerNameText>{server.name}</ServerNameText>
<ServerIcon>
<CodeOutlined />
<MonitorCheck size={16} color={server.isActive ? 'var(--color-primary)' : 'var(--color-text-3)'} />
</ServerIcon>
<ServerName>{server.name}</ServerName>
<StatusIndicator>
<IndicatorLight
size={6}
color={server.isActive ? 'green' : 'var(--color-text-3)'}
animation={server.isActive}
shadow={false}
/>
</StatusIndicator>
</ServerHeader>
<ServerDescription>{server.description}</ServerDescription>
</ServerCard>
)}
</DragableList>
</ServersList>
<ServerSettings>{selectedMcpServer && <McpSettings server={selectedMcpServer} />}</ServerSettings>
</ServerName>
<StatusIndicator>
<Button
icon={<Settings2 size={16} />}
type="text"
onClick={() => navigate(`/settings/mcp/settings`, { state: { server } })}
/>
</StatusIndicator>
</ServerHeader>
<ServerDescription>{server.description}</ServerDescription>
<ServerFooter>
<Tag color="default" style={{ borderRadius: 20, margin: 0 }}>
{t(`settings.mcp.types.${server.type || 'stdio'}`)}
</Tag>
</ServerFooter>
</ServerCard>
)}
</DragableList>
{mcpServers.length === 0 && (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={t('settings.mcp.noServers')}
style={{ marginTop: 20 }}
/>
)}
</Container>
)
}
const Container = styled(HStack)`
const Container = styled(Scrollbar)`
display: flex;
flex: 1;
width: 350px;
flex-direction: column;
width: 100%;
height: calc(100vh - var(--navbar-height));
overflow: hidden;
`
const ServersList = styled(Scrollbar)`
gap: 16px;
display: flex;
flex-direction: column;
height: calc(100vh - var(--navbar-height));
width: 350px;
padding: 15px;
border-right: 0.5px solid var(--color-border);
`
const ServerSettings = styled(VStack)`
flex: 1;
height: calc(100vh - var(--navbar-height));
padding: 20px;
padding-top: 15px;
gap: 15px;
overflow-y: auto;
`
const ListHeader = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
h2 {
font-size: 20px;
font-size: 22px;
margin: 0;
}
`
@ -112,19 +111,17 @@ const ListHeader = styled.div`
const ServerCard = styled.div`
display: flex;
flex-direction: column;
border: 1px solid var(--color-border);
border-radius: 8px;
border: 0.5px solid var(--color-border);
border-radius: var(--list-item-border-radius);
padding: 10px 16px;
cursor: pointer;
transition: all 0.2s ease;
height: 120px;
background-color: var(--color-background);
margin-bottom: 5px;
height: 125px;
cursor: pointer;
&:hover,
&.active {
&:hover {
border-color: var(--color-primary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
`
@ -136,16 +133,24 @@ const ServerHeader = styled.div`
const ServerIcon = styled.div`
font-size: 18px;
color: var(--color-primary);
margin-right: 8px;
display: flex;
`
const ServerName = styled.div`
font-weight: 500;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
align-items: center;
gap: 10px;
`
const ServerNameText = styled.span`
font-size: 15px;
font-weight: 500;
font-family: Ubuntu;
`
const StatusIndicator = styled.div`
@ -161,21 +166,14 @@ const ServerDescription = styled.div`
-webkit-box-orient: vertical;
width: 100%;
word-break: break-word;
height: 50px;
`
const AddServerCard = styled(ServerCard)`
const ServerFooter = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-style: dashed;
background-color: transparent;
color: var(--color-text-2);
`
const AddServerText = styled.div`
margin-top: 12px;
font-weight: 500;
justify-content: space-between;
margin-top: 10px;
`
export default McpServersList

View File

@ -1,11 +1,12 @@
import { DeleteOutlined, SaveOutlined } from '@ant-design/icons'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { MCPPrompt, MCPResource, MCPServer, MCPTool } from '@renderer/types'
import { Button, Flex, Form, Input, Radio, Switch, Tabs } from 'antd'
import TextArea from 'antd/es/input/TextArea'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
import { useLocation, useNavigate } from 'react-router'
import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '..'
@ -13,10 +14,6 @@ import MCPPromptsSection from './McpPrompt'
import MCPResourcesSection from './McpResource'
import MCPToolsSection from './McpTool'
interface Props {
server: MCPServer
}
interface MCPFormValues {
name: string
description?: string
@ -61,8 +58,9 @@ const parseKeyValueString = (str: string): Record<string, string> => {
return result
}
const McpSettings: React.FC<Props> = ({ server }) => {
const McpSettings: React.FC = () => {
const { t } = useTranslation()
const { server } = useLocation().state as { server: MCPServer }
const { deleteMCPServer, updateMCPServer } = useMCPServers()
const [serverType, setServerType] = useState<MCPServer['type']>('stdio')
const [form] = Form.useForm<MCPFormValues>()
@ -77,6 +75,8 @@ const McpSettings: React.FC<Props> = ({ server }) => {
const [isShowRegistry, setIsShowRegistry] = useState(false)
const [registry, setRegistry] = useState<Registry[]>()
const { theme } = useTheme()
const navigate = useNavigate()
useEffect(() => {
@ -538,8 +538,8 @@ const McpSettings: React.FC<Props> = ({ server }) => {
}
return (
<SettingContainer style={{ width: '100%' }}>
<SettingGroup style={{ marginBottom: 0 }}>
<SettingContainer theme={theme} style={{ width: '100%', paddingTop: 55, backgroundColor: 'transparent' }}>
<SettingGroup style={{ marginBottom: 0, borderRadius: 'var(--list-item-border-radius)' }}>
<SettingTitle>
<Flex justify="space-between" align="center" gap={5} style={{ marginRight: 10 }}>
<ServerName className="text-nowrap">{server?.name}</ServerName>
@ -557,18 +557,18 @@ const McpSettings: React.FC<Props> = ({ server }) => {
icon={<SaveOutlined />}
onClick={onSave}
loading={loading}
shape="round"
disabled={!isFormChanged || activeTab !== 'settings'}>
{t('common.save')}
</Button>
</Flex>
</SettingTitle>
<SettingDivider />
<Tabs
defaultActiveKey="settings"
items={tabs}
onChange={(key) => setActiveTab(key as TabKey)}
style={{ marginTop: 8 }}
style={{ marginTop: 8, backgroundColor: 'transparent' }}
/>
</SettingGroup>
</SettingContainer>

View File

@ -1,13 +1,11 @@
import { EditOutlined, ExportOutlined } from '@ant-design/icons'
import { NavbarRight } from '@renderer/components/app/Navbar'
import { HStack } from '@renderer/components/Layout'
import { isWindows } from '@renderer/config/constant'
import { Button } from 'antd'
import { Search } from 'lucide-react'
import { Search, SquareArrowOutUpRight } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
import EditMcpJsonPopup from './EditMcpJsonPopup'
import InstallNpxUv from './InstallNpxUv'
export const McpSettingsNavbar = () => {
@ -27,20 +25,11 @@ export const McpSettingsNavbar = () => {
style={{ fontSize: 13, height: 28, borderRadius: 20 }}>
{t('settings.mcp.searchNpx')}
</Button>
<Button
size="small"
type="text"
onClick={() => EditMcpJsonPopup.show()}
icon={<EditOutlined />}
className="nodrag"
style={{ fontSize: 13, height: 28, borderRadius: 20 }}>
{t('settings.mcp.editMcpJson')}
</Button>
<Button
size="small"
type="text"
onClick={onClick}
icon={<ExportOutlined />}
icon={<SquareArrowOutUpRight size={14} />}
className="nodrag"
style={{ fontSize: 13, height: 28, borderRadius: 20 }}>
{t('settings.mcp.findMore')}

View File

@ -1,6 +1,6 @@
import { CheckOutlined, PlusOutlined } from '@ant-design/icons'
import { nanoid } from '@reduxjs/toolkit'
import npmLogo from '@renderer/assets/images/mcp/npm.svg'
import logo from '@renderer/assets/images/cherry-text-logo.svg'
import { Center, HStack } from '@renderer/components/Layout'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { builtinMCPServers } from '@renderer/store/mcp'
@ -27,9 +27,7 @@ const npmScopes = ['@cherry', '@modelcontextprotocol', '@gongrzhe', '@mcpmarket'
let _searchResults: SearchResult[] = []
const NpxSearch: FC<{
setSelectedMcpServer: (server: MCPServer) => void
}> = ({ setSelectedMcpServer }) => {
const NpxSearch: FC = () => {
const { t } = useTranslation()
const { Text, Link } = Typography
@ -126,7 +124,7 @@ const NpxSearch: FC<{
<Center>
<Space direction="vertical" style={{ marginBottom: 25, width: 500 }}>
<Center style={{ marginBottom: 15 }}>
<img src={npmLogo} alt="npm" width={100} />
<img src={logo} alt="npm" width={120} />
</Center>
<Space.Compact style={{ width: '100%' }}>
<Input
@ -142,7 +140,6 @@ const NpxSearch: FC<{
{npmScopes.map((scope) => (
<Tag
key={scope}
bordered={false}
onClick={() => {
setNpmScope(scope)
handleNpmSearch(scope)
@ -171,6 +168,7 @@ const NpxSearch: FC<{
<Card
size="small"
key={record.name}
style={{ borderRadius: 'var(--list-item-border-radius)' }}
title={
<Typography.Title level={5} style={{ margin: 0 }} className="selectable">
{record.name}
@ -178,7 +176,7 @@ const NpxSearch: FC<{
}
extra={
<Flex>
<Tag bordered={false} color="processing">
<Tag color="success" style={{ borderRadius: 100 }}>
v{record.version}
</Tag>
<Button
@ -197,7 +195,6 @@ const NpxSearch: FC<{
if (buildInServer) {
addMCPServer(buildInServer)
window.message.success({ content: t('settings.mcp.addSuccess'), key: 'mcp-add-server' })
setSelectedMcpServer(buildInServer)
return
}
@ -214,7 +211,6 @@ const NpxSearch: FC<{
addMCPServer(newServer)
window.message.success({ content: t('settings.mcp.addSuccess'), key: 'mcp-add-server' })
setSelectedMcpServer(newServer)
}}
/>
</Flex>
@ -242,6 +238,7 @@ const Container = styled.div`
flex: 1;
flex-direction: column;
gap: 8px;
padding-top: 20px;
`
const ResultList = styled.div`

View File

@ -1,10 +1,7 @@
import { ArrowLeftOutlined } from '@ant-design/icons'
import { VStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { MCPServer } from '@renderer/types'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Button } from 'antd'
import { FC } from 'react'
import { Route, Routes, useLocation } from 'react-router'
import { Link } from 'react-router-dom'
import styled from 'styled-components'
@ -12,58 +9,35 @@ import styled from 'styled-components'
import { SettingContainer } from '..'
import InstallNpxUv from './InstallNpxUv'
import McpServersList from './McpServersList'
import McpSettings from './McpSettings'
import NpxSearch from './NpxSearch'
const MCPSettings: FC = () => {
const { t } = useTranslation()
const { mcpServers } = useMCPServers()
const [selectedMcpServer, setSelectedMcpServer] = useState<MCPServer | null>(mcpServers[0])
const { theme } = useTheme()
const location = useLocation()
const pathname = location.pathname
useEffect(() => {
const _selectedMcpServer = mcpServers.find((server) => server.id === selectedMcpServer?.id)
setSelectedMcpServer(_selectedMcpServer || mcpServers[0])
}, [mcpServers, selectedMcpServer])
// Check if the selected server still exists in the updated mcpServers list
useEffect(() => {
if (selectedMcpServer) {
const serverExists = mcpServers.some((server) => server.id === selectedMcpServer.id)
if (!serverExists) {
setSelectedMcpServer(mcpServers[0])
}
}
}, [mcpServers, selectedMcpServer])
const isHome = pathname === '/settings/mcp'
return (
<Container>
<SettingContainer theme={theme} style={{ padding: 0, position: 'relative' }}>
{!isHome && (
<BackButtonContainer>
<Link to="/settings/mcp">
<BackButton>
<ArrowLeftOutlined /> {t('common.back')}
</BackButton>
<Button type="default" icon={<ArrowLeftOutlined />} shape="circle" />
</Link>
</BackButtonContainer>
)}
<MainContainer>
<Routes>
<Route
path="/"
element={
<McpServersList selectedMcpServer={selectedMcpServer} setSelectedMcpServer={setSelectedMcpServer} />
}
/>
<Route path="/" element={<McpServersList />} />
<Route path="settings" element={<McpSettings />} />
<Route
path="npx-search"
element={
<SettingContainer theme={theme}>
<NpxSearch setSelectedMcpServer={setSelectedMcpServer} />
<NpxSearch />
</SettingContainer>
}
/>
@ -77,18 +51,20 @@ const MCPSettings: FC = () => {
/>
</Routes>
</MainContainer>
</Container>
</SettingContainer>
)
}
const Container = styled(VStack)`
flex: 1;
`
const BackButtonContainer = styled.div`
padding: 12px 0 0 12px;
width: 100%;
background-color: var(--color-background);
display: flex;
align-items: center;
padding: 10px 20px;
background-color: transparent;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1000;
`
const MainContainer = styled.div`
@ -97,21 +73,4 @@ const MainContainer = styled.div`
width: 100%;
`
const BackButton = styled.div`
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--color-text-1);
cursor: pointer;
padding: 6px 12px;
border-radius: 4px;
margin-bottom: 10px;
background-color: var(--color-bg-1);
&:hover {
color: var(--color-primary);
background-color: var(--color-bg-2);
}
`
export default MCPSettings