From fe0c0fac1e903599b4333bad23a8f47108a38b3f Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Wed, 24 Sep 2025 12:24:28 +0800 Subject: [PATCH 01/12] fix(assistant): enforce id requirement when updating assistant (#10321) * fix(assistant): enforce id requirement when updating assistant Ensure assistant id is always provided when updating assistant properties by making it a required field in the update payload. This prevents potential bugs where updates might be applied to wrong assistants. * refactor(useAssistant): simplify updateAssistant callback by removing redundant id Update InputbarTools to use simplified callback signature --- src/renderer/src/hooks/useAssistant.ts | 5 ++++- src/renderer/src/store/assistants.ts | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/hooks/useAssistant.ts b/src/renderer/src/hooks/useAssistant.ts index 096c91b5a1..8a038bec31 100644 --- a/src/renderer/src/hooks/useAssistant.ts +++ b/src/renderer/src/hooks/useAssistant.ts @@ -172,7 +172,10 @@ export function useAssistant(id: string) { (model: Model) => assistant && dispatch(setModel({ assistantId: assistant?.id, model })), [assistant, dispatch] ), - updateAssistant: useCallback((assistant: Partial) => dispatch(updateAssistant(assistant)), [dispatch]), + updateAssistant: useCallback( + (update: Partial>) => dispatch(updateAssistant({ id, ...update })), + [dispatch, id] + ), updateAssistantSettings } } diff --git a/src/renderer/src/store/assistants.ts b/src/renderer/src/store/assistants.ts index 8f53977835..5462f4bb3e 100644 --- a/src/renderer/src/store/assistants.ts +++ b/src/renderer/src/store/assistants.ts @@ -46,8 +46,9 @@ const assistantsSlice = createSlice({ removeAssistant: (state, action: PayloadAction<{ id: string }>) => { state.assistants = state.assistants.filter((c) => c.id !== action.payload.id) }, - updateAssistant: (state, action: PayloadAction>) => { - state.assistants = state.assistants.map((c) => (c.id === action.payload.id ? { ...c, ...action.payload } : c)) + updateAssistant: (state, action: PayloadAction & { id: string }>) => { + const { id, ...update } = action.payload + state.assistants = state.assistants.map((c) => (c.id === id ? { ...c, ...update } : c)) }, updateAssistantSettings: ( state, From ec4d106a59c5a14b20a8239720d85cf255e788ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=C2=B7Dong?= <98630204+GeorgeDong32@users.noreply.github.com> Date: Wed, 24 Sep 2025 14:19:06 +0800 Subject: [PATCH 02/12] fix(minapps): openMinApp function doesn't work properly (#10308) * feat(minapps): support temporary minapps * feat(settings): use openSmartMinApp with app logo to open docs sites * refactor(icons): replace styled img with tailwind * feat(tab): tighten types * feat(tab): use minapps cache and log missing entries * test(icons): update MinAppIcon snapshot to reflect class and attrs --- .../src/components/Icons/MinAppIcon.tsx | 64 ++++++++++++------- .../__snapshots__/MinAppIcon.test.tsx.snap | 12 ++-- .../src/components/Tab/TabContainer.tsx | 57 +++++++++++++++-- src/renderer/src/hooks/useMinappPopup.ts | 32 ++++++++++ src/renderer/src/pages/minapps/MinAppPage.tsx | 15 ++++- .../src/pages/settings/AboutSettings.tsx | 10 +-- .../settings/DataSettings/JoplinSettings.tsx | 8 ++- .../settings/DataSettings/NotionSettings.tsx | 8 ++- .../settings/DataSettings/S3Settings.tsx | 8 ++- .../settings/DataSettings/SiyuanSettings.tsx | 8 ++- .../settings/DataSettings/YuqueSettings.tsx | 8 ++- 11 files changed, 170 insertions(+), 60 deletions(-) diff --git a/src/renderer/src/components/Icons/MinAppIcon.tsx b/src/renderer/src/components/Icons/MinAppIcon.tsx index c9612416bb..98974da745 100644 --- a/src/renderer/src/components/Icons/MinAppIcon.tsx +++ b/src/renderer/src/components/Icons/MinAppIcon.tsx @@ -1,7 +1,6 @@ import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { MinAppType } from '@renderer/types' import { FC } from 'react' -import styled from 'styled-components' interface Props { app: MinAppType @@ -11,31 +10,52 @@ interface Props { } const MinAppIcon: FC = ({ app, size = 48, style, sidebar = false }) => { + // First try to find in DEFAULT_MIN_APPS for predefined styling const _app = DEFAULT_MIN_APPS.find((item) => item.id === app.id) - if (!_app) { - return null + // If found in DEFAULT_MIN_APPS, use predefined styling + if (_app) { + return ( + {app.name + ) } - return ( - - ) + // If not found in DEFAULT_MIN_APPS but app has logo, use it (for temporary apps) + if (app.logo) { + return ( + {app.name + ) + } + + return null } -const Container = styled.img` - border-radius: 16px; - user-select: none; - -webkit-user-drag: none; -` - export default MinAppIcon diff --git a/src/renderer/src/components/Icons/__tests__/__snapshots__/MinAppIcon.test.tsx.snap b/src/renderer/src/components/Icons/__tests__/__snapshots__/MinAppIcon.test.tsx.snap index e41515fed6..3395e366f4 100644 --- a/src/renderer/src/components/Icons/__tests__/__snapshots__/MinAppIcon.test.tsx.snap +++ b/src/renderer/src/components/Icons/__tests__/__snapshots__/MinAppIcon.test.tsx.snap @@ -1,15 +1,11 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`MinAppIcon > should render correctly with various props 1`] = ` -.c0 { - border-radius: 16px; - user-select: none; - -webkit-user-drag: none; -} - Test App `; diff --git a/src/renderer/src/components/Tab/TabContainer.tsx b/src/renderer/src/components/Tab/TabContainer.tsx index efa19565d1..da2ad668de 100644 --- a/src/renderer/src/components/Tab/TabContainer.tsx +++ b/src/renderer/src/components/Tab/TabContainer.tsx @@ -1,4 +1,5 @@ import { PlusOutlined } from '@ant-design/icons' +import { loggerService } from '@logger' import { Sortable, useDndReorder } from '@renderer/components/dnd' import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer' import { isMac } from '@renderer/config/constant' @@ -12,9 +13,10 @@ import tabsService from '@renderer/services/TabsService' import { useAppDispatch, useAppSelector } from '@renderer/store' import type { Tab } from '@renderer/store/tabs' import { addTab, removeTab, setActiveTab, setTabs } from '@renderer/store/tabs' -import { ThemeMode } from '@renderer/types' +import { MinAppType, ThemeMode } from '@renderer/types' import { classNames } from '@renderer/utils' import { Tooltip } from 'antd' +import { LRUCache } from 'lru-cache' import { FileSearch, Folder, @@ -45,14 +47,40 @@ interface TabsContainerProps { children: React.ReactNode } -const getTabIcon = (tabId: string, minapps: any[]): React.ReactNode | undefined => { +const logger = loggerService.withContext('TabContainer') + +const getTabIcon = ( + tabId: string, + minapps: MinAppType[], + minAppsCache?: LRUCache +): React.ReactNode | undefined => { // Check if it's a minapp tab (format: apps:appId) if (tabId.startsWith('apps:')) { const appId = tabId.replace('apps:', '') - const app = [...DEFAULT_MIN_APPS, ...minapps].find((app) => app.id === appId) + let app = [...DEFAULT_MIN_APPS, ...minapps].find((app) => app.id === appId) + + // If not found in permanent apps, search in temporary apps cache + // The cache stores apps opened via openSmartMinapp() for top navbar mode + // These are temporary MinApps that were opened but not yet saved to user's config + // The cache is LRU (Least Recently Used) with max size from settings + // Cache validity: Apps in cache are currently active/recently used, not outdated + if (!app && minAppsCache) { + app = minAppsCache.get(appId) + + // Defensive programming: If app not found in cache but tab exists, + // the cache entry may have been evicted due to LRU policy + // Log warning for debugging potential sync issues + if (!app) { + logger.warn(`MinApp ${appId} not found in cache, using fallback icon`) + } + } + if (app) { return } + + // Fallback: If no app found (cache evicted), show default icon + return } switch (tabId) { @@ -94,7 +122,7 @@ const TabsContainer: React.FC = ({ children }) => { const activeTabId = useAppSelector((state) => state.tabs.activeTabId) const isFullscreen = useFullscreen() const { settedTheme, toggleTheme } = useTheme() - const { hideMinappPopup } = useMinappPopup() + const { hideMinappPopup, minAppsCache } = useMinappPopup() const { minapps } = useMinapps() const { t } = useTranslation() @@ -112,8 +140,23 @@ const TabsContainer: React.FC = ({ children }) => { // Check if it's a minapp tab if (tabId.startsWith('apps:')) { const appId = tabId.replace('apps:', '') - const app = [...DEFAULT_MIN_APPS, ...minapps].find((app) => app.id === appId) - return app ? app.name : 'MinApp' + let app = [...DEFAULT_MIN_APPS, ...minapps].find((app) => app.id === appId) + + // If not found in permanent apps, search in temporary apps cache + // This ensures temporary MinApps display proper titles while being used + // The LRU cache automatically manages app lifecycle and prevents memory leaks + if (!app && minAppsCache) { + app = minAppsCache.get(appId) + + // Defensive programming: If app not found in cache but tab exists, + // the cache entry may have been evicted due to LRU policy + if (!app) { + logger.warn(`MinApp ${appId} not found in cache, using fallback title`) + } + } + + // Return app name if found, otherwise use fallback with appId + return app ? app.name : `MinApp-${appId}` } return getTitleLabel(tabId) } @@ -196,7 +239,7 @@ const TabsContainer: React.FC = ({ children }) => { renderItem={(tab) => ( handleTabClick(tab)}> - {tab.id && {getTabIcon(tab.id, minapps)}} + {tab.id && {getTabIcon(tab.id, minapps, minAppsCache)}} {getTabTitle(tab.id)} {tab.id !== 'home' && ( diff --git a/src/renderer/src/hooks/useMinappPopup.ts b/src/renderer/src/hooks/useMinappPopup.ts index e8765267a6..99b49e43e8 100644 --- a/src/renderer/src/hooks/useMinappPopup.ts +++ b/src/renderer/src/hooks/useMinappPopup.ts @@ -1,6 +1,7 @@ import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { useRuntime } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' // 使用设置中的值 +import NavigationService from '@renderer/services/NavigationService' import TabsService from '@renderer/services/TabsService' import { useAppDispatch } from '@renderer/store' import { @@ -14,6 +15,8 @@ import { clearWebviewState } from '@renderer/utils/webviewStateManager' import { LRUCache } from 'lru-cache' import { useCallback } from 'react' +import { useNavbarPosition } from './useSettings' + let minAppsCache: LRUCache /** @@ -34,6 +37,7 @@ export const useMinappPopup = () => { const dispatch = useAppDispatch() const { openedKeepAliveMinapps, openedOneOffMinapp, minappShow } = useRuntime() const { maxKeepAliveMinapps } = useSettings() // 使用设置中的值 + const { isTopNavbar } = useNavbarPosition() const createLRUCache = useCallback(() => { return new LRUCache({ @@ -165,6 +169,33 @@ export const useMinappPopup = () => { dispatch(setMinappShow(false)) }, [dispatch, minappShow, openedOneOffMinapp]) + /** Smart open minapp that adapts to navbar position */ + const openSmartMinapp = useCallback( + (config: MinAppType, keepAlive: boolean = false) => { + if (isTopNavbar) { + // For top navbar mode, need to add to cache first for temporary apps + const cacheApp = minAppsCache.get(config.id) + if (!cacheApp) { + // Add temporary app to cache so MinAppPage can find it + minAppsCache.set(config.id, config) + } + + // Set current minapp and show state + dispatch(setCurrentMinappId(config.id)) + dispatch(setMinappShow(true)) + + // Then navigate to the app tab using NavigationService + if (NavigationService.navigate) { + NavigationService.navigate(`/apps/${config.id}`) + } + } else { + // For side navbar, use the traditional popup system + openMinapp(config, keepAlive) + } + }, + [isTopNavbar, openMinapp, dispatch] + ) + return { openMinapp, openMinappKeepAlive, @@ -172,6 +203,7 @@ export const useMinappPopup = () => { closeMinapp, hideMinappPopup, closeAllMinapps, + openSmartMinapp, // Expose cache instance for TabsService integration minAppsCache } diff --git a/src/renderer/src/pages/minapps/MinAppPage.tsx b/src/renderer/src/pages/minapps/MinAppPage.tsx index 9629b3c56d..c85afab22c 100644 --- a/src/renderer/src/pages/minapps/MinAppPage.tsx +++ b/src/renderer/src/pages/minapps/MinAppPage.tsx @@ -44,11 +44,20 @@ const MinAppPage: FC = () => { } }, [isTopNavbar]) - // Find the app from all available apps + // Find the app from all available apps (including cached ones) const app = useMemo(() => { if (!appId) return null - return [...DEFAULT_MIN_APPS, ...minapps].find((app) => app.id === appId) - }, [appId, minapps]) + + // First try to find in default and custom mini-apps + let foundApp = [...DEFAULT_MIN_APPS, ...minapps].find((app) => app.id === appId) + + // If not found and we have cache, try to find in cache (for temporary apps) + if (!foundApp && minAppsCache) { + foundApp = minAppsCache.get(appId) + } + + return foundApp + }, [appId, minapps, minAppsCache]) useEffect(() => { // If app not found, redirect to apps list diff --git a/src/renderer/src/pages/settings/AboutSettings.tsx b/src/renderer/src/pages/settings/AboutSettings.tsx index b79f1cb099..40b0a99ecb 100644 --- a/src/renderer/src/pages/settings/AboutSettings.tsx +++ b/src/renderer/src/pages/settings/AboutSettings.tsx @@ -14,7 +14,7 @@ import { runAsyncFunction } from '@renderer/utils' import { UpgradeChannel } from '@shared/config/constant' import { Avatar, Button, Progress, Radio, Row, Switch, Tag, Tooltip } from 'antd' import { debounce } from 'lodash' -import { Bug, FileCheck, Github, Globe, Mail, Rss } from 'lucide-react' +import { Bug, FileCheck, Globe, Mail, Rss } from 'lucide-react' import { BadgeQuestionMark } from 'lucide-react' import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -32,7 +32,7 @@ const AboutSettings: FC = () => { const { theme } = useTheme() const dispatch = useAppDispatch() const { update } = useRuntime() - const { openMinapp } = useMinappPopup() + const { openSmartMinapp } = useMinappPopup() const onCheckUpdate = debounce( async () => { @@ -79,7 +79,7 @@ const AboutSettings: FC = () => { const showLicense = async () => { const { appPath } = await window.api.getAppInfo() - openMinapp({ + openSmartMinapp({ id: 'cherrystudio-license', name: t('settings.about.license.title'), url: `file://${appPath}/resources/cherry-studio/license.html`, @@ -89,7 +89,7 @@ const AboutSettings: FC = () => { const showReleases = async () => { const { appPath } = await window.api.getAppInfo() - openMinapp({ + openSmartMinapp({ id: 'cherrystudio-releases', name: t('settings.about.releases.title'), url: `file://${appPath}/resources/cherry-studio/releases.html?theme=${theme === ThemeMode.dark ? 'dark' : 'light'}`, @@ -309,7 +309,7 @@ const AboutSettings: FC = () => { - + {t('settings.about.feedback.title')} )} @@ -482,7 +482,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => { selectedPainting={painting} onSelectPainting={onSelectPainting} onDeletePainting={onDeletePainting} - onNewPainting={createNewPainting} + onNewPainting={handleAddPainting} /> @@ -556,12 +556,6 @@ const ToolbarMenu = styled.div` gap: 8px; ` -const Title = styled.h1` - margin: 0; - font-size: 18px; - font-weight: 600; -` - const ProviderTitleContainer = styled.div` display: flex; justify-content: space-between; From 09e9b95e08130672248b50fa848c10747a66539c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=C2=B7Dong?= <98630204+GeorgeDong32@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:38:13 +0800 Subject: [PATCH 07/12] fix(reasoning): thinking control for ds v3.1 of tencent platform (#10333) * feat(reasoning): add Hunyuan and Tencent TI thinking config * fix: style * fix(reasoning): merge same type providers --- src/renderer/src/aiCore/utils/reasoning.ts | 2 ++ src/renderer/src/config/models/reasoning.ts | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/aiCore/utils/reasoning.ts b/src/renderer/src/aiCore/utils/reasoning.ts index bee07b1e0d..18f303c0a3 100644 --- a/src/renderer/src/aiCore/utils/reasoning.ts +++ b/src/renderer/src/aiCore/utils/reasoning.ts @@ -112,6 +112,8 @@ export function getReasoningEffort(assistant: Assistant, model: Model): Reasonin return { enable_thinking: true } + case SystemProviderIds.hunyuan: + case SystemProviderIds['tencent-cloud-ti']: case SystemProviderIds.doubao: return { thinking: { diff --git a/src/renderer/src/config/models/reasoning.ts b/src/renderer/src/config/models/reasoning.ts index 74bddd2897..25f13c86e4 100644 --- a/src/renderer/src/config/models/reasoning.ts +++ b/src/renderer/src/config/models/reasoning.ts @@ -93,7 +93,17 @@ export function isSupportedThinkingTokenModel(model?: Model): boolean { // Specifically for DeepSeek V3.1. White list for now if (isDeepSeekHybridInferenceModel(model)) { return ( - ['openrouter', 'dashscope', 'modelscope', 'doubao', 'silicon', 'nvidia', 'ppio'] satisfies SystemProviderId[] + [ + 'openrouter', + 'dashscope', + 'modelscope', + 'doubao', + 'silicon', + 'nvidia', + 'ppio', + 'hunyuan', + 'tencent-cloud-ti' + ] satisfies SystemProviderId[] ).some((id) => id === model.provider) } From 2bafc53b25063eade007f93df2d76cb6f19a3b5b Mon Sep 17 00:00:00 2001 From: "Johnny.H" Date: Wed, 24 Sep 2025 23:27:07 +0800 Subject: [PATCH 08/12] Show loading icon when chat is in streaming (#10319) * support chat stream loading rendering * support chat stream loading rendering * update loading icon to dots * fix format --------- Co-authored-by: suyao --- .../home/Messages/Blocks/PlaceholderBlock.tsx | 4 ++-- .../src/pages/home/Messages/Blocks/index.tsx | 21 +++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/pages/home/Messages/Blocks/PlaceholderBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/PlaceholderBlock.tsx index bcc8a96859..7682ae2343 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/PlaceholderBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/PlaceholderBlock.tsx @@ -1,4 +1,4 @@ -import { LoadingIcon } from '@renderer/components/Icons' +import { Spinner } from '@heroui/react' import { MessageBlockStatus, MessageBlockType, type PlaceholderMessageBlock } from '@renderer/types/newMessage' import React from 'react' import styled from 'styled-components' @@ -10,7 +10,7 @@ const PlaceholderBlock: React.FC = ({ block }) => { if (block.status === MessageBlockStatus.PROCESSING && block.type === MessageBlockType.UNKNOWN) { return ( - + ) } diff --git a/src/renderer/src/pages/home/Messages/Blocks/index.tsx b/src/renderer/src/pages/home/Messages/Blocks/index.tsx index 5d6128e660..0e2d318e1e 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/index.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/index.tsx @@ -3,7 +3,7 @@ import type { RootState } from '@renderer/store' import { messageBlocksSelectors } from '@renderer/store/messageBlock' import type { ImageMessageBlock, Message, MessageBlock } from '@renderer/types/newMessage' import { MessageBlockStatus, MessageBlockType } from '@renderer/types/newMessage' -import { isMainTextBlock, isVideoBlock } from '@renderer/utils/messageUtils/is' +import { isMainTextBlock, isMessageProcessing, isVideoBlock } from '@renderer/utils/messageUtils/is' import { AnimatePresence, motion, type Variants } from 'motion/react' import React, { useMemo } from 'react' import { useSelector } from 'react-redux' @@ -107,6 +107,9 @@ const MessageBlockRenderer: React.FC = ({ blocks, message }) => { const renderedBlocks = blocks.map((blockId) => blockEntities[blockId]).filter(Boolean) const groupedBlocks = useMemo(() => groupSimilarBlocks(renderedBlocks), [renderedBlocks]) + // Check if message is still processing + const isProcessing = isMessageProcessing(message) + return ( {groupedBlocks.map((block) => { @@ -151,9 +154,6 @@ const MessageBlockRenderer: React.FC = ({ blocks, message }) => { switch (block.type) { case MessageBlockType.UNKNOWN: - if (block.status === MessageBlockStatus.PROCESSING) { - blockComponent = - } break case MessageBlockType.MAIN_TEXT: case MessageBlockType.CODE: { @@ -213,6 +213,19 @@ const MessageBlockRenderer: React.FC = ({ blocks, message }) => { ) })} + {isProcessing && ( + + + + )} ) } From a3a26c69c5142eed9883e65a2861150416c48a99 Mon Sep 17 00:00:00 2001 From: SuYao Date: Thu, 25 Sep 2025 10:55:31 +0800 Subject: [PATCH 09/12] fix: seed think (#10322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 添加 seedThink 标签以支持新的模型识别 * Enable reasoning for SEED-OSS models - Add SEED-OSS model ID check to reasoning exclusion logic - Include SEED-OSS models in reasoning model detection * fix: 更新 reasoning-end 事件处理以使用最终推理内容 --- src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts | 2 +- src/renderer/src/aiCore/middleware/AiSdkMiddlewareBuilder.ts | 4 +++- src/renderer/src/aiCore/utils/reasoning.ts | 2 +- src/renderer/src/config/models/reasoning.ts | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts b/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts index c27362eb14..8e35496ae6 100644 --- a/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts +++ b/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts @@ -170,7 +170,7 @@ export class AiSdkToChunkAdapter { case 'reasoning-end': this.onChunk({ type: ChunkType.THINKING_COMPLETE, - text: (chunk.providerMetadata?.metadata?.thinking_content as string) || '', + text: (chunk.providerMetadata?.metadata?.thinking_content as string) || final.reasoningContent, thinking_millsec: (chunk.providerMetadata?.metadata?.thinking_millsec as number) || 0 }) final.reasoningContent = '' diff --git a/src/renderer/src/aiCore/middleware/AiSdkMiddlewareBuilder.ts b/src/renderer/src/aiCore/middleware/AiSdkMiddlewareBuilder.ts index 1f18e49bad..20b89cf2e5 100644 --- a/src/renderer/src/aiCore/middleware/AiSdkMiddlewareBuilder.ts +++ b/src/renderer/src/aiCore/middleware/AiSdkMiddlewareBuilder.ts @@ -143,12 +143,14 @@ export function buildAiSdkMiddlewares(config: AiSdkMiddlewareConfig): LanguageMo const tagName = { reasoning: 'reasoning', think: 'think', - thought: 'thought' + thought: 'thought', + seedThink: 'seed:think' } function getReasoningTagName(modelId: string | undefined): string { if (modelId?.includes('gpt-oss')) return tagName.reasoning if (modelId?.includes('gemini')) return tagName.thought + if (modelId?.includes('seed-oss-36b')) return tagName.seedThink return tagName.think } diff --git a/src/renderer/src/aiCore/utils/reasoning.ts b/src/renderer/src/aiCore/utils/reasoning.ts index 18f303c0a3..9328f7f0ce 100644 --- a/src/renderer/src/aiCore/utils/reasoning.ts +++ b/src/renderer/src/aiCore/utils/reasoning.ts @@ -52,7 +52,7 @@ export function getReasoningEffort(assistant: Assistant, model: Model): Reasonin return {} } // Don't disable reasoning for models that require it - if (isGrokReasoningModel(model) || isOpenAIReasoningModel(model)) { + if (isGrokReasoningModel(model) || isOpenAIReasoningModel(model) || model.id.includes('seed-oss')) { return {} } return { reasoning: { enabled: false, exclude: true } } diff --git a/src/renderer/src/config/models/reasoning.ts b/src/renderer/src/config/models/reasoning.ts index 25f13c86e4..607df8fd95 100644 --- a/src/renderer/src/config/models/reasoning.ts +++ b/src/renderer/src/config/models/reasoning.ts @@ -391,7 +391,8 @@ export function isReasoningModel(model?: Model): boolean { isDeepSeekHybridInferenceModel(model) || modelId.includes('magistral') || modelId.includes('minimax-m1') || - modelId.includes('pangu-pro-moe') + modelId.includes('pangu-pro-moe') || + modelId.includes('seed-oss') ) { return true } From 0a149e3d9e5c495dc81e8fe791cb340dbc6ce8ab Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 25 Sep 2025 10:36:34 +0800 Subject: [PATCH 10/12] chore: release v1.6.0 --- electron-builder.yml | 35 ++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index b08ecb5563..9b9a239160 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -125,13 +125,30 @@ afterSign: scripts/notarize.js artifactBuildCompleted: scripts/artifact-build-completed.js releaseInfo: releaseNotes: | - 🎨 界面优化: - - 优化了多个组件的布局和间距,提升视觉体验 - - 改进了导航栏和标签栏的样式显示 - - MCP 服务器卡片宽度调整为 100%,提高响应式布局效果 - - 优化了笔记侧边栏的滚动行为 + 🚀 New Features: + - Refactored AI core engine for more efficient and stable content generation + - Added support for multiple AI model providers: CherryIN, AiOnly + - Added API server functionality for external application integration + - Added PaddleOCR document recognition for enhanced document processing + - Added Anthropic OAuth authentication support + - Added data storage space limit notifications + - Added font settings for global and code fonts customization + - Added auto-copy feature after translation completion + - Added keyboard shortcuts: rename topic, edit last message, etc. + - Added text attachment preview for viewing file contents in messages + - Added custom window control buttons (minimize, maximize, close) + - Support for Qwen long-text (qwen-long) and document analysis (qwen-doc) models with native file uploads + - Support for Qwen image recognition models (Qwen-Image) + - Added iFlow CLI support + - Converted knowledge base and web search to tool-calling approach for better flexibility + + 🎨 UI Improvements & Bug Fixes: + - Integrated HeroUI and Tailwind CSS framework + - Optimized message notification styles with unified toast component + - Moved free models to bottom with fixed position for easier access + - Refactored quick panel and input bar tools for smoother operation + - Optimized responsive design for navbar and sidebar + - Improved scrollbar component with horizontal scrolling support + - Fixed multiple translation issues: paste handling, file processing, state management + - Various UI optimizations and bug fixes - 🐛 问题修复: - - 修复了小应用打开功能无法正常工作的问题 - - 修复了助手更新时 ID 丢失导致更新失败的问题 - - 确保助手更新时 ID 字段为必填项,防止数据错误 diff --git a/package.json b/package.json index b33ebc8940..dfe28b8f27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CherryStudio", - "version": "1.6.0-rc.5", + "version": "1.6.0", "private": true, "description": "A powerful AI assistant for producer.", "main": "./out/main/index.js", From 2ed99c0cb841b996d54c064f849cad5415824b77 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:28:51 +0800 Subject: [PATCH 11/12] ci(workflow): only trigger PR CI on non-draft PRs (#10338) ci(workflow): only trigger PR CI on non-draft PRs and specific events Add trigger conditions for PR CI workflow to run on non-draft PRs and specific event types --- .github/workflows/pr-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 137208bff0..4f462db95c 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -10,12 +10,14 @@ on: - main - develop - v2 + types: [ready_for_review, synchronize, opened] jobs: build: runs-on: ubuntu-latest env: PRCI: true + if: github.event.pull_request.draft == false steps: - name: Check out Git repository From 0f8cbeed110fb11f8939f8978de0a9b47d9fb111 Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Thu, 25 Sep 2025 13:44:17 +0800 Subject: [PATCH 12/12] fix(translate): remove unused effect for clearing translation contenton mount (#10349) * fix(translate): remove unused effect for clearing translation content on mount * format code --- src/renderer/src/pages/translate/TranslatePage.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/renderer/src/pages/translate/TranslatePage.tsx b/src/renderer/src/pages/translate/TranslatePage.tsx index da0056b39d..e883955eb1 100644 --- a/src/renderer/src/pages/translate/TranslatePage.tsx +++ b/src/renderer/src/pages/translate/TranslatePage.tsx @@ -335,12 +335,6 @@ const TranslatePage: FC = () => { setTargetLanguage(source) }, [couldExchangeAuto, detectedLanguage, sourceLanguage, t, targetLanguage]) - // Clear translation content when component mounts - useEffect(() => { - setText('') - setTranslatedContent('') - }, []) - useEffect(() => { isEmpty(text) && setTranslatedContent('') }, [setTranslatedContent, text])