From 52b5c4a360c5c829bb5d8e0bad12c32b60f35b1c Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Wed, 30 Jul 2025 17:21:44 +0800 Subject: [PATCH 1/6] fix: update express dependency in package.json (#8677) chore: update express dependency in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 93b21a23fd..ba47244ed5 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "@libsql/client": "0.14.0", "@libsql/win32-x64-msvc": "^0.4.7", "@strongtz/win32-arm64-msvc": "^0.4.7", + "express": "^5.1.0", "graceful-fs": "^4.2.11", "jsdom": "26.1.0", "node-stream-zip": "^1.15.0", @@ -202,7 +203,6 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-unused-imports": "^4.1.4", - "express": "^5.1.0", "fast-diff": "^1.3.0", "fast-xml-parser": "^5.2.0", "fetch-socks": "1.3.2", From 236a6bdcb0247893d5efc0114f8ac0a007892cbc Mon Sep 17 00:00:00 2001 From: one Date: Wed, 30 Jul 2025 19:15:08 +0800 Subject: [PATCH 2/6] perf(ModelList): provider settings and model list responsiveness (#8667) - improve responsiveness for provider switching - improve ModelList performance - use startTransition - use virtual list for model groups - remove excessive ModelEditContent, add EditModelPopup - add model count - improve model management popup - rename to ManageModelsPopup - use virtual list with sticky group name - use SvgSpinners180Ring for spin --- .../src/components/CustomCollapse.tsx | 7 +- .../components/ModelList/EditModelPopup.tsx | 57 ++++ .../components/ModelList/ManageModelsList.tsx | 281 ++++++++++++++++++ ...tModelsPopup.tsx => ManageModelsPopup.tsx} | 160 ++-------- .../src/components/ModelList/ModelList.tsx | 161 ++++++---- .../components/ModelList/ModelListGroup.tsx | 89 +++++- .../src/components/ModelList/index.ts | 3 +- .../src/components/ModelList/utils.ts | 10 + .../pages/settings/ProviderSettings/index.tsx | 13 +- 9 files changed, 558 insertions(+), 223 deletions(-) create mode 100644 src/renderer/src/components/ModelList/EditModelPopup.tsx create mode 100644 src/renderer/src/components/ModelList/ManageModelsList.tsx rename src/renderer/src/components/ModelList/{EditModelsPopup.tsx => ManageModelsPopup.tsx} (69%) create mode 100644 src/renderer/src/components/ModelList/utils.ts diff --git a/src/renderer/src/components/CustomCollapse.tsx b/src/renderer/src/components/CustomCollapse.tsx index c6f4f79a78..d41a9ffd60 100644 --- a/src/renderer/src/components/CustomCollapse.tsx +++ b/src/renderer/src/components/CustomCollapse.tsx @@ -11,6 +11,7 @@ interface CustomCollapseProps { defaultActiveKey?: string[] activeKey?: string[] collapsible?: 'header' | 'icon' | 'disabled' + onChange?: (activeKeys: string | string[]) => void style?: React.CSSProperties styles?: { header?: React.CSSProperties @@ -26,6 +27,7 @@ const CustomCollapse: FC = ({ defaultActiveKey = ['1'], activeKey, collapsible = undefined, + onChange, style, styles }) => { @@ -78,7 +80,10 @@ const CustomCollapse: FC = ({ activeKey={activeKey} destroyInactivePanel={destroyInactivePanel} collapsible={collapsible} - onChange={setActiveKeys} + onChange={(keys) => { + setActiveKeys(keys) + onChange?.(keys) + }} expandIcon={({ isActive }) => ( void +} + +const PopupContainer: React.FC = ({ provider, model, resolve }) => { + const handleUpdateModel = (updatedModel: Model) => { + resolve(updatedModel) + } + + const handleClose = () => { + resolve(undefined) // Resolve with no data on close + } + + return ( + + ) +} + +const TopViewKey = 'EditModelPopup' + +export default class EditModelPopup { + static hide() { + TopView.hide(TopViewKey) + } + + static show(props: ShowParams) { + return new Promise((resolve) => { + TopView.show( + { + resolve(v) + this.hide() + }} + />, + TopViewKey + ) + }) + } +} diff --git a/src/renderer/src/components/ModelList/ManageModelsList.tsx b/src/renderer/src/components/ModelList/ManageModelsList.tsx new file mode 100644 index 0000000000..bede6b5b74 --- /dev/null +++ b/src/renderer/src/components/ModelList/ManageModelsList.tsx @@ -0,0 +1,281 @@ +import { MinusOutlined, PlusOutlined } from '@ant-design/icons' +import CustomTag from '@renderer/components/CustomTag' +import ExpandableText from '@renderer/components/ExpandableText' +import ModelIdWithTags from '@renderer/components/ModelIdWithTags' +import NewApiBatchAddModelPopup from '@renderer/components/ModelList/NewApiBatchAddModelPopup' +import { getModelLogo } from '@renderer/config/models' +import FileItem from '@renderer/pages/files/FileItem' +import { Model, Provider } from '@renderer/types' +import { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual' +import { Button, Flex, Tooltip } from 'antd' +import { Avatar } from 'antd' +import { ChevronRight } from 'lucide-react' +import React, { memo, useCallback, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import { isModelInProvider, isValidNewApiModel } from './utils' + +// 列表项类型定义 +interface GroupRowData { + type: 'group' + groupName: string + models: Model[] +} + +interface ModelRowData { + type: 'model' + model: Model +} + +type RowData = GroupRowData | ModelRowData + +interface ManageModelsListProps { + modelGroups: Record + provider: Provider + onAddModel: (model: Model) => void + onRemoveModel: (model: Model) => void +} + +const ManageModelsList: React.FC = ({ modelGroups, provider, onAddModel, onRemoveModel }) => { + const { t } = useTranslation() + const scrollerRef = useRef(null) + const activeStickyIndexRef = useRef(0) + const [collapsedGroups, setCollapsedGroups] = useState(new Set()) + + const handleGroupToggle = useCallback((groupName: string) => { + setCollapsedGroups((prev) => { + const newSet = new Set(prev) + if (newSet.has(groupName)) { + newSet.delete(groupName) // 如果已折叠,则展开 + } else { + newSet.add(groupName) // 如果已展开,则折叠 + } + return newSet + }) + }, []) + + // 将分组数据扁平化为单一列表,过滤掉空组 + const flatRows = useMemo(() => { + const rows: RowData[] = [] + + Object.entries(modelGroups).forEach(([groupName, models]) => { + if (models.length > 0) { + // 只添加非空组 + rows.push({ type: 'group', groupName, models }) + if (!collapsedGroups.has(groupName)) { + models.forEach((model) => { + rows.push({ type: 'model', model }) + }) + } + } + }) + + return rows + }, [modelGroups, collapsedGroups]) + + // 找到所有组 header 的索引 + const stickyIndexes = useMemo(() => { + return flatRows.map((row, index) => (row.type === 'group' ? index : -1)).filter((index) => index !== -1) + }, [flatRows]) + + const isSticky = useCallback((index: number) => stickyIndexes.includes(index), [stickyIndexes]) + + const isActiveSticky = useCallback((index: number) => activeStickyIndexRef.current === index, []) + + // 自定义 range extractor 用于 sticky header + const rangeExtractor = useCallback( + (range: any) => { + activeStickyIndexRef.current = [...stickyIndexes].reverse().find((index) => range.startIndex >= index) ?? 0 + const next = new Set([activeStickyIndexRef.current, ...defaultRangeExtractor(range)]) + return [...next].sort((a, b) => a - b) + }, + [stickyIndexes] + ) + + const virtualizer = useVirtualizer({ + count: flatRows.length, + getScrollElement: () => scrollerRef.current, + estimateSize: () => 42, + rangeExtractor, + overscan: 5 + }) + + const renderGroupTools = useCallback( + (models: Model[]) => { + const isAllInProvider = models.every((model) => isModelInProvider(provider, model.id)) + + const handleGroupAction = () => { + if (isAllInProvider) { + // 移除整组 + models.filter((model) => isModelInProvider(provider, model.id)).forEach(onRemoveModel) + } else { + // 添加整组 + const wouldAddModels = models.filter((model) => !isModelInProvider(provider, model.id)) + + if (provider.id === 'new-api') { + if (wouldAddModels.every(isValidNewApiModel)) { + wouldAddModels.forEach(onAddModel) + } else { + NewApiBatchAddModelPopup.show({ + title: t('settings.models.add.batch_add_models'), + batchModels: wouldAddModels, + provider + }) + } + } else { + wouldAddModels.forEach(onAddModel) + } + } + } + + return ( + + - {models.map((model) => ( - setEditingModel(null)} - key={model.id} - /> - ))} ) } diff --git a/src/renderer/src/components/ModelList/ModelListGroup.tsx b/src/renderer/src/components/ModelList/ModelListGroup.tsx index b4a84ce77d..edf15e8f8a 100644 --- a/src/renderer/src/components/ModelList/ModelListGroup.tsx +++ b/src/renderer/src/components/ModelList/ModelListGroup.tsx @@ -2,8 +2,9 @@ import { MinusOutlined } from '@ant-design/icons' import CustomCollapse from '@renderer/components/CustomCollapse' import { Model } from '@renderer/types' import { ModelWithStatus } from '@renderer/types/healthCheck' +import { useVirtualizer } from '@tanstack/react-virtual' import { Button, Flex, Tooltip } from 'antd' -import React, { memo } from 'react' +import React, { memo, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -31,11 +32,35 @@ const ModelListGroup: React.FC = ({ onRemoveGroup }) => { const { t } = useTranslation() + const scrollerRef = useRef(null) + const [isExpanded, setIsExpanded] = useState(defaultOpen) + + const virtualizer = useVirtualizer({ + count: models.length, + getScrollElement: () => scrollerRef.current, + estimateSize: () => 52, + overscan: 5 + }) + + const virtualItems = virtualizer.getVirtualItems() + + // 监听折叠面板状态变化,确保虚拟列表在展开时正确渲染 + useEffect(() => { + if (isExpanded && scrollerRef.current) { + requestAnimationFrame(() => virtualizer.measure()) + } + }, [isExpanded, virtualizer]) + + const handleCollapseChange = (activeKeys: string[] | string) => { + const isNowExpanded = Array.isArray(activeKeys) ? activeKeys.length > 0 : !!activeKeys + setIsExpanded(isNowExpanded) + } return ( {groupName} @@ -52,18 +77,45 @@ const ModelListGroup: React.FC = ({ /> }> - - {models.map((model) => ( - status.model.id === model.id)} - onEdit={onEditModel} - onRemove={onRemoveModel} - disabled={disabled} - /> - ))} - + +
+
+ {virtualItems.map((virtualItem) => { + const model = models[virtualItem.index] + return ( +
+ status.model.id === model.id)} + onEdit={onEditModel} + onRemove={onRemoveModel} + disabled={disabled} + /> +
+ ) + })} +
+
+
) @@ -79,6 +131,17 @@ const CustomCollapseWrapper = styled.div` &:hover .toolbar-item { opacity: 1; } + + /* 移除 collapse 的 padding,转而在 scroller 内部调整 */ + .ant-collapse-content-box { + padding: 0 !important; + } +` + +const ScrollContainer = styled.div` + overflow-y: auto; + max-height: 390px; + padding: 4px 16px; ` export default memo(ModelListGroup) diff --git a/src/renderer/src/components/ModelList/index.ts b/src/renderer/src/components/ModelList/index.ts index db47dd39d3..937c8f6617 100644 --- a/src/renderer/src/components/ModelList/index.ts +++ b/src/renderer/src/components/ModelList/index.ts @@ -1,6 +1,7 @@ export { default as AddModelPopup } from './AddModelPopup' -export { default as EditModelsPopup } from './EditModelsPopup' +export { default as EditModelPopup } from './EditModelPopup' export { default as HealthCheckPopup } from './HealthCheckPopup' +export { default as ManageModelsPopup } from './ManageModelsPopup' export { default as ModelEditContent } from './ModelEditContent' export { default as ModelList } from './ModelList' export { default as NewApiAddModelPopup } from './NewApiAddModelPopup' diff --git a/src/renderer/src/components/ModelList/utils.ts b/src/renderer/src/components/ModelList/utils.ts new file mode 100644 index 0000000000..3ced576219 --- /dev/null +++ b/src/renderer/src/components/ModelList/utils.ts @@ -0,0 +1,10 @@ +import { Model, Provider } from '@renderer/types' + +// Check if the model exists in the provider's model list +export const isModelInProvider = (provider: Provider, modelId: string): boolean => { + return provider.models.some((m) => m.id === modelId) +} + +export const isValidNewApiModel = (model: Model): boolean => { + return !!(model.supported_endpoint_types && model.supported_endpoint_types.length > 0) +} diff --git a/src/renderer/src/pages/settings/ProviderSettings/index.tsx b/src/renderer/src/pages/settings/ProviderSettings/index.tsx index 351c5a58f2..711f503c86 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/index.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/index.tsx @@ -19,7 +19,7 @@ import { } from '@renderer/utils' import { Avatar, Button, Card, Dropdown, Input, MenuProps, Tag } from 'antd' import { Eye, EyeOff, Search, UserPen } from 'lucide-react' -import { FC, useEffect, useState } from 'react' +import { FC, startTransition, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useSearchParams } from 'react-router-dom' import styled from 'styled-components' @@ -34,12 +34,19 @@ const ProvidersList: FC = () => { const [searchParams] = useSearchParams() const providers = useAllProviders() const { updateProviders, addProvider, removeProvider, updateProvider } = useProviders() - const [selectedProvider, setSelectedProvider] = useState(providers[0]) + const [selectedProvider, _setSelectedProvider] = useState(providers[0]) const { t } = useTranslation() const [searchText, setSearchText] = useState('') const [dragging, setDragging] = useState(false) const [providerLogos, setProviderLogos] = useState>({}) + const setSelectedProvider = useCallback( + (provider: Provider) => { + startTransition(() => _setSelectedProvider(provider)) + }, + [_setSelectedProvider] + ) + useEffect(() => { const loadAllLogos = async () => { const logos: Record = {} @@ -71,7 +78,7 @@ const ProvidersList: FC = () => { setSelectedProvider(providers[0]) } } - }, [providers, searchParams]) + }, [providers, searchParams, setSelectedProvider]) // Handle provider add key from URL schema useEffect(() => { From 80409cd94eebfcc10a5ba0255d3bf859f5ac3ef8 Mon Sep 17 00:00:00 2001 From: one Date: Wed, 30 Jul 2025 20:32:24 +0800 Subject: [PATCH 3/6] fix(ModelList): stop propagation (#8685) --- src/renderer/src/components/ModelList/ModelListGroup.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/ModelList/ModelListGroup.tsx b/src/renderer/src/components/ModelList/ModelListGroup.tsx index edf15e8f8a..1e92a7f9b3 100644 --- a/src/renderer/src/components/ModelList/ModelListGroup.tsx +++ b/src/renderer/src/components/ModelList/ModelListGroup.tsx @@ -72,7 +72,10 @@ const ModelListGroup: React.FC = ({ type="text" className="toolbar-item" icon={} - onClick={onRemoveGroup} + onClick={(e) => { + e.stopPropagation() + onRemoveGroup() + }} disabled={disabled} /> From 7ae7f13ad1f66620a24289b24bb676d920a29b96 Mon Sep 17 00:00:00 2001 From: SuYao Date: Thu, 31 Jul 2025 09:50:24 +0800 Subject: [PATCH 4/6] refactor(ApiService): optimize memory search handling and improve error logging (#8671) * refactor(ApiService): optimize memory search handling and improve error logging - Updated fetchExternalTool to handle memory search in a separate promise, ensuring better management of asynchronous operations. - Enhanced error logging for memory search failures to improve debugging. - Removed redundant memory search result storage logic from the main result handling section. * refactor(ApiService): streamline external tool fetching and memory search handling - Simplified the fetchExternalTool function by separating web and knowledge search calls into individual await statements. - Improved memory search handling by awaiting the searchMemory function directly, ensuring better asynchronous flow. - Updated result storage logic to ensure memory search results are stored correctly after fetching. --- src/renderer/src/services/ApiService.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index 841d9de934..8976780449 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -312,16 +312,18 @@ async function fetchExternalTool( let memorySearchReferences: MemoryItem[] | undefined const parentSpanId = currentSpan(lastUserMessage.topicId, assistant.model?.name)?.spanContext().spanId - // 并行执行搜索 - if (shouldWebSearch || shouldKnowledgeSearch || shouldSearchMemory) { - ;[webSearchResponseFromSearch, knowledgeReferencesFromSearch, memorySearchReferences] = await Promise.all([ - searchTheWeb(extractResults, parentSpanId), - searchKnowledgeBase(extractResults, parentSpanId, assistant.model?.name), - searchMemory() - ]) + if (shouldWebSearch) { + webSearchResponseFromSearch = await searchTheWeb(extractResults, parentSpanId) + } + + if (shouldKnowledgeSearch) { + knowledgeReferencesFromSearch = await searchKnowledgeBase(extractResults, parentSpanId, assistant.model?.name) + } + + if (shouldSearchMemory) { + memorySearchReferences = await searchMemory() } - // 存储搜索结果 if (lastUserMessage) { if (webSearchResponseFromSearch) { window.keyv.set(`web-search-${lastUserMessage.id}`, webSearchResponseFromSearch) @@ -492,7 +494,7 @@ export async function fetchChatCompletion({ // Post-conversation memory processing const globalMemoryEnabled = selectGlobalMemoryEnabled(store.getState()) if (globalMemoryEnabled && assistant.enableMemory) { - await processConversationMemory(messages, assistant) + processConversationMemory(messages, assistant) } return await AI.completionsForTrace(completionsParams, requestOptions) From eb4f218c7d9cfa51b1286cb08331c8a2ee5fc389 Mon Sep 17 00:00:00 2001 From: LiuVaayne <10231735+vaayne@users.noreply.github.com> Date: Thu, 31 Jul 2025 09:54:23 +0800 Subject: [PATCH 5/6] feat: make API server default to closed/disabled (#8691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat: make API server default to closed/disabled - Add `enabled` field to ApiServerConfig interface with default value false - Update Redux store to include apiServer.enabled setting (defaults to false) - Add setApiServerEnabled action to control server state - Modify main process to only auto-start API server if explicitly enabled - Update UI to show enable/disable toggle in API server settings - Add migrations to handle existing configurations - Server controls now only visible when API server is enabled The API server will no longer start automatically on application launch unless explicitly enabled by the user through the settings interface. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * 🔧 chore: improve build and lint commands - Move typecheck and i18n checks to lint command for better developer experience - Simplify build:check to focus on linting and testing - Ensure all quality checks run together during development 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --------- Co-authored-by: Claude --- package.json | 4 ++-- src/main/apiServer/config.ts | 3 +++ src/main/index.ts | 8 ++++++-- .../settings/ApiServerSettings/ApiServerSettings.tsx | 3 ++- src/renderer/src/store/migrate.ts | 1 + src/renderer/src/store/settings.ts | 8 ++++++++ src/renderer/src/types/index.ts | 1 + 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ba47244ed5..7727d0a13f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "dev": "dotenv electron-vite dev", "debug": "electron-vite -- --inspect --sourcemap --remote-debugging-port=9222", "build": "npm run typecheck && electron-vite build", - "build:check": "yarn typecheck && yarn check:i18n && yarn test", + "build:check": "yarn lint && yarn test", "build:unpack": "dotenv npm run build && electron-builder --dir", "build:win": "dotenv npm run build && electron-builder --win --x64 --arm64", "build:win:x64": "dotenv npm run build && electron-builder --win --x64", @@ -66,7 +66,7 @@ "test:lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts", "test:scripts": "vitest scripts", "format": "prettier --write .", - "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", + "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix && yarn typecheck && yarn check:i18n", "prepare": "git config blame.ignoreRevsFile .git-blame-ignore-revs && husky" }, "dependencies": { diff --git a/src/main/apiServer/config.ts b/src/main/apiServer/config.ts index d9b73d521a..15d7a244a3 100644 --- a/src/main/apiServer/config.ts +++ b/src/main/apiServer/config.ts @@ -22,12 +22,14 @@ class ConfigManager { }) this._config = { + enabled: settings?.apiServer?.enabled ?? false, port: settings?.apiServer?.port ?? 23333, host: 'localhost', apiKey: generatedKey } } else { this._config = { + enabled: settings?.apiServer?.enabled ?? false, port: settings?.apiServer?.port ?? 23333, host: 'localhost', apiKey: settings.apiServer.apiKey @@ -38,6 +40,7 @@ class ConfigManager { } catch (error: any) { logger.warn('Failed to load config from Redux, using defaults:', error) this._config = { + enabled: false, port: 23333, host: 'localhost', apiKey: `cs-sk-${uuidv4()}` diff --git a/src/main/index.ts b/src/main/index.ts index 5b61299e4f..d724efdbec 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -143,9 +143,13 @@ if (!app.requestSingleInstanceLock()) { // Start API server if enabled try { - await apiServerService.start() + const config = await apiServerService.getCurrentConfig() + logger.info('API server config:', config) + if (config.enabled) { + await apiServerService.start() + } } catch (error: any) { - logger.error('Failed to start API server:', error) + logger.error('Failed to check/start API server:', error) } }) diff --git a/src/renderer/src/pages/settings/ApiServerSettings/ApiServerSettings.tsx b/src/renderer/src/pages/settings/ApiServerSettings/ApiServerSettings.tsx index f612e2f93d..2fb22e9588 100644 --- a/src/renderer/src/pages/settings/ApiServerSettings/ApiServerSettings.tsx +++ b/src/renderer/src/pages/settings/ApiServerSettings/ApiServerSettings.tsx @@ -1,7 +1,7 @@ import { useTheme } from '@renderer/context/ThemeProvider' import { loggerService } from '@renderer/services/LoggerService' import { RootState, useAppDispatch } from '@renderer/store' -import { setApiServerApiKey, setApiServerPort } from '@renderer/store/settings' +import { setApiServerApiKey, setApiServerEnabled, setApiServerPort } from '@renderer/store/settings' import { IpcChannel } from '@shared/IpcChannel' import { Button, Input, InputNumber, Tooltip, Typography } from 'antd' import { Copy, ExternalLink, Play, RotateCcw, Square } from 'lucide-react' @@ -64,6 +64,7 @@ const ApiServerSettings: FC = () => { } catch (error) { window.message.error(t('apiServer.messages.operationFailed') + (error as Error).message) } finally { + dispatch(setApiServerEnabled(enabled)) setApiServerLoading(false) } } diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 12d0069a36..a4cd0ba5d8 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -1925,6 +1925,7 @@ const migrateConfig = { // Initialize API server configuration if not present if (!state.settings.apiServer) { state.settings.apiServer = { + enabled: false, host: 'localhost', port: 23333, apiKey: `cs-sk-${uuid()}` diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 4e366e7edc..62437cbbcc 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -382,6 +382,7 @@ export const initialState: SettingsState = { navbarPosition: 'left', // API Server apiServer: { + enabled: false, host: 'localhost', port: 23333, apiKey: `cs-sk-${uuid()}` @@ -791,6 +792,12 @@ const settingsSlice = createSlice({ state.navbarPosition = action.payload }, // API Server actions + setApiServerEnabled: (state, action: PayloadAction) => { + state.apiServer = { + ...state.apiServer, + enabled: action.payload + } + }, setApiServerPort: (state, action: PayloadAction) => { state.apiServer = { ...state.apiServer, @@ -926,6 +933,7 @@ export const { setEnableDeveloperMode, setNavbarPosition, // API Server actions + setApiServerEnabled, setApiServerPort, setApiServerApiKey } = settingsSlice.actions diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 9eff1e5c6a..d2ee286be7 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -842,6 +842,7 @@ export type S3Config = { export type { Message } from './newMessage' export interface ApiServerConfig { + enabled: boolean host: string port: number apiKey: string From 6d0867c27db821d1eb20e51b2ed99778b381ee84 Mon Sep 17 00:00:00 2001 From: one Date: Thu, 31 Jul 2025 10:31:12 +0800 Subject: [PATCH 6/6] fix: do not exit on emoji picker (#8702) * fix: do not exit on emoji picker * fix: emoji picker in default assistant setting --- src/renderer/src/pages/agents/components/AddAgentPopup.tsx | 3 ++- .../pages/settings/ModelSettings/DefaultAssistantSettings.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/pages/agents/components/AddAgentPopup.tsx b/src/renderer/src/pages/agents/components/AddAgentPopup.tsx index 087ab3d6c6..2550e82f11 100644 --- a/src/renderer/src/pages/agents/components/AddAgentPopup.tsx +++ b/src/renderer/src/pages/agents/components/AddAgentPopup.tsx @@ -199,7 +199,8 @@ const PopupContainer: React.FC = ({ resolve }) => { }} /> } - arrow> + arrow + trigger="click"> diff --git a/src/renderer/src/pages/settings/ModelSettings/DefaultAssistantSettings.tsx b/src/renderer/src/pages/settings/ModelSettings/DefaultAssistantSettings.tsx index edf96505bd..026d148448 100644 --- a/src/renderer/src/pages/settings/ModelSettings/DefaultAssistantSettings.tsx +++ b/src/renderer/src/pages/settings/ModelSettings/DefaultAssistantSettings.tsx @@ -109,7 +109,7 @@ const AssistantSettings: FC = () => { theme={theme}> {t('common.name')} - } arrow> + } arrow trigger="click"> {emoji && (