Merge branch 'main' of github.com:CherryHQ/cherry-studio into v2

This commit is contained in:
fullex 2025-10-31 11:12:49 +08:00
commit 23f7b39753
22 changed files with 181 additions and 129 deletions

View File

@ -77,7 +77,7 @@ Please review the following critical information before submitting your Pull Req
Our core team is currently focused on significant architectural updates that involve these data structures. To ensure stability and focus during this period, contributions of this nature will be temporarily managed internally.
* **PRs that require changes to Redux state shape or IndexedDB schemas will be closed.**
* **This restriction is temporary and will be lifted with the release of `v2.0.0`.** You can track the progress of `v2.0.0` and its related discussions on issue [#10162](https://github.com/YOUR_ORG/YOUR_REPO/issues/10162) (please replace with your actual repo link).
* **This restriction is temporary and will be lifted with the release of `v2.0.0`.** You can track the progress of `v2.0.0` and its related discussions on issue [#10162](https://github.com/CherryHQ/cherry-studio/pull/10162).
We highly encourage contributions for:
* Bug fixes 🐞

View File

@ -81,7 +81,7 @@ git commit --signoff -m "Your commit message"
我们的核心团队目前正专注于涉及这些数据结构的关键架构更新和基础工作。为确保在此期间的稳定性与专注,此类贡献将暂时由内部进行管理。
* **需要更改 Redux 状态结构或 IndexedDB schema 的 PR 将会被关闭。**
* **此限制是临时性的,并将在 `v2.0.0` 版本发布后解除。** 您可以通过 Issue [#10162](https://github.com/YOUR_ORG/YOUR_REPO/issues/10162) (请替换为您的实际仓库链接) 跟踪 `v2.0.0` 的进展及相关讨论。
* **此限制是临时性的,并将在 `v2.0.0` 版本发布后解除。** 您可以通过 Issue [#10162](https://github.com/CherryHQ/cherry-studio/pull/10162) 跟踪 `v2.0.0` 的进展及相关讨论。
我们非常鼓励以下类型的贡献:
* 错误修复 🐞

View File

@ -334,13 +334,13 @@
--cs-ring: hsla(84, 81%, 44%, 0.4);
/* UI Element Colors */
--cs-secondary: hsla(0, 0%, 0%, 0.05); /* Secondary Button Background */
--cs-secondary: hsla(0, 0%, 0%, 0.05); /* Secondary Button Background */
--cs-secondary-hover: hsla(0, 0%, 0%, 0.85);
--cs-secondary-active: hsla(0, 0%, 0%, 0.7);
--cs-muted: hsla(0, 0%, 0%, 0.05); /* Muted/Subtle Background */
--cs-accent: hsla(0, 0%, 0%, 0.05); /* Accent Background */
--cs-ghost-hover: hsla(0, 0%, 0%, 0.05); /* Ghost Button Hover */
--cs-ghost-active: hsla(0, 0%, 0%, 0.1); /* Ghost Button Active */
--cs-muted: hsla(0, 0%, 0%, 0.05); /* Muted/Subtle Background */
--cs-accent: hsla(0, 0%, 0%, 0.05); /* Accent Background */
--cs-ghost-hover: hsla(0, 0%, 0%, 0.05); /* Ghost Button Hover */
--cs-ghost-active: hsla(0, 0%, 0%, 0.1); /* Ghost Button Active */
/* Sidebar */
--cs-sidebar: var(--cs-white);
@ -390,43 +390,43 @@
/* Spacing & Sizing (合并) */
/* ==================== */
--cs-size-5xs: 0.25rem; /* 4px */
--cs-size-4xs: 0.5rem; /* 8px */
--cs-size-3xs: 0.75rem; /* 12px */
--cs-size-2xs: 1rem; /* 16px */
--cs-size-xs: 1.5rem; /* 24px */
--cs-size-sm: 2rem; /* 32px */
--cs-size-md: 2.5rem; /* 40px */
--cs-size-lg: 3rem; /* 48px */
--cs-size-xl: 3.5rem; /* 56px */
--cs-size-2xl: 4rem; /* 64px */
--cs-size-3xl: 4.5rem; /* 72px */
--cs-size-4xl: 5rem; /* 80px */
--cs-size-5xl: 5.5rem; /* 88px */
--cs-size-6xl: 6rem; /* 96px */
--cs-size-7xl: 6.5rem; /* 104px */
--cs-size-8xl: 7rem; /* 112px */
--cs-size-5xs: 0.25rem; /* 4px */
--cs-size-4xs: 0.5rem; /* 8px */
--cs-size-3xs: 0.75rem; /* 12px */
--cs-size-2xs: 1rem; /* 16px */
--cs-size-xs: 1.5rem; /* 24px */
--cs-size-sm: 2rem; /* 32px */
--cs-size-md: 2.5rem; /* 40px */
--cs-size-lg: 3rem; /* 48px */
--cs-size-xl: 3.5rem; /* 56px */
--cs-size-2xl: 4rem; /* 64px */
--cs-size-3xl: 4.5rem; /* 72px */
--cs-size-4xl: 5rem; /* 80px */
--cs-size-5xl: 5.5rem; /* 88px */
--cs-size-6xl: 6rem; /* 96px */
--cs-size-7xl: 6.5rem; /* 104px */
--cs-size-8xl: 7rem; /* 112px */
/* ==================== */
/* Border Radius */
/* ==================== */
--cs-radius-4xs: 0.25rem; /* 4px */
--cs-radius-3xs: 0.5rem; /* 8px */
--cs-radius-2xs: 0.75rem; /* 12px */
--cs-radius-xs: 1rem; /* 16px */
--cs-radius-sm: 1.5rem; /* 24px */
--cs-radius-md: 2rem; /* 32px */
--cs-radius-lg: 2.5rem; /* 40px */
--cs-radius-xl: 3rem; /* 48px */
--cs-radius-2xl: 3.5rem; /* 56px */
--cs-radius-3xl: 4rem; /* 64px */
--cs-radius-round: 999px; /* 保持 px因为是特殊值 */
--cs-radius-4xs: 0.25rem; /* 4px */
--cs-radius-3xs: 0.5rem; /* 8px */
--cs-radius-2xs: 0.75rem; /* 12px */
--cs-radius-xs: 1rem; /* 16px */
--cs-radius-sm: 1.5rem; /* 24px */
--cs-radius-md: 2rem; /* 32px */
--cs-radius-lg: 2.5rem; /* 40px */
--cs-radius-xl: 3rem; /* 48px */
--cs-radius-2xl: 3.5rem; /* 56px */
--cs-radius-3xl: 4rem; /* 64px */
--cs-radius-round: 999px; /* 保持 px因为是特殊值 */
/* Border Width */
--cs-border-width-sm: 1px; /* 1px */
--cs-border-width-md: 2px; /* 2px */
--cs-border-width-lg: 3px; /* 3px */
--cs-border-width-sm: 1px; /* 1px */
--cs-border-width-md: 2px; /* 2px */
--cs-border-width-lg: 3px; /* 3px */
/* ==================== */
/* Typography */
@ -442,43 +442,43 @@
--cs-font-weight-bold: 700;
/* Font Sizes - Body */
--cs-font-size-body-xs: 0.75rem; /* 12px */
--cs-font-size-body-sm: 0.875rem; /* 14px */
--cs-font-size-body-md: 1rem; /* 16px */
--cs-font-size-body-lg: 1.125rem; /* 18px */
--cs-font-size-body-xs: 0.75rem; /* 12px */
--cs-font-size-body-sm: 0.875rem; /* 14px */
--cs-font-size-body-md: 1rem; /* 16px */
--cs-font-size-body-lg: 1.125rem; /* 18px */
/* Font Sizes - Heading */
--cs-font-size-heading-xs: 1.25rem; /* 20px */
--cs-font-size-heading-sm: 1.5rem; /* 24px */
--cs-font-size-heading-md: 2rem; /* 32px */
--cs-font-size-heading-lg: 2.5rem; /* 40px */
--cs-font-size-heading-xl: 3rem; /* 48px */
--cs-font-size-heading-2xl: 3.75rem; /* 60px */
--cs-font-size-heading-xs: 1.25rem; /* 20px */
--cs-font-size-heading-sm: 1.5rem; /* 24px */
--cs-font-size-heading-md: 2rem; /* 32px */
--cs-font-size-heading-lg: 2.5rem; /* 40px */
--cs-font-size-heading-xl: 3rem; /* 48px */
--cs-font-size-heading-2xl: 3.75rem; /* 60px */
/* Line Heights - Body */
--cs-line-height-body-xs: 1.25rem; /* 20px */
--cs-line-height-body-sm: 1.5rem; /* 24px */
--cs-line-height-body-md: 1.5rem; /* 24px */
--cs-line-height-body-lg: 1.75rem; /* 28px */
--cs-line-height-body-xs: 1.25rem; /* 20px */
--cs-line-height-body-sm: 1.5rem; /* 24px */
--cs-line-height-body-md: 1.5rem; /* 24px */
--cs-line-height-body-lg: 1.75rem; /* 28px */
/* Line Heights - Heading */
--cs-line-height-heading-xs: 2rem; /* 32px */
--cs-line-height-heading-sm: 2.5rem; /* 40px */
--cs-line-height-heading-md: 3rem; /* 48px */
--cs-line-height-heading-xs: 2rem; /* 32px */
--cs-line-height-heading-sm: 2.5rem; /* 40px */
--cs-line-height-heading-md: 3rem; /* 48px */
--cs-line-height-heading-lg: 3.75rem; /* 60px */
--cs-line-height-heading-xl: 5rem; /* 80px */
--cs-line-height-heading-xl: 5rem; /* 80px */
/* Paragraph Spacing */
--cs-paragraph-spacing-body-xs: 0.75rem; /* 12px */
--cs-paragraph-spacing-body-sm: 0.875rem; /* 14px */
--cs-paragraph-spacing-body-md: 1rem; /* 16px */
--cs-paragraph-spacing-body-lg: 1.125rem; /* 18px */
--cs-paragraph-spacing-heading-xs: 1.25rem; /* 20px */
--cs-paragraph-spacing-heading-sm: 1.5rem; /* 24px */
--cs-paragraph-spacing-heading-md: 2rem; /* 32px */
--cs-paragraph-spacing-heading-lg: 2.5rem; /* 40px */
--cs-paragraph-spacing-heading-xl: 3rem; /* 48px */
--cs-paragraph-spacing-heading-2xl: 3.75rem; /* 60px */
--cs-paragraph-spacing-body-xs: 0.75rem; /* 12px */
--cs-paragraph-spacing-body-sm: 0.875rem; /* 14px */
--cs-paragraph-spacing-body-md: 1rem; /* 16px */
--cs-paragraph-spacing-body-lg: 1.125rem; /* 18px */
--cs-paragraph-spacing-heading-xs: 1.25rem; /* 20px */
--cs-paragraph-spacing-heading-sm: 1.5rem; /* 24px */
--cs-paragraph-spacing-heading-md: 2rem; /* 32px */
--cs-paragraph-spacing-heading-lg: 2.5rem; /* 40px */
--cs-paragraph-spacing-heading-xl: 3rem; /* 48px */
--cs-paragraph-spacing-heading-2xl: 3.75rem; /* 60px */
}
/* ==================== */
@ -506,13 +506,13 @@
--cs-ring: hsla(84, 81%, 44%, 0.4);
/* UI Element Colors - Dark Mode */
--cs-secondary: hsla(0, 0%, 100%, 0.1); /* Secondary Button Background */
--cs-secondary: hsla(0, 0%, 100%, 0.1); /* Secondary Button Background */
--cs-secondary-hover: hsla(0, 0%, 100%, 0.2);
--cs-secondary-active: hsla(0, 0%, 100%, 0.25);
--cs-muted: hsla(0, 0%, 100%, 0.1); /* Muted/Subtle Background */
--cs-accent: hsla(0, 0%, 100%, 0.1); /* Accent Background */
--cs-ghost-hover: hsla(0, 0%, 100%, 0.1); /* Ghost Button Hover */
--cs-ghost-active: hsla(0, 0%, 100%, 0.15); /* Ghost Button Active */
--cs-muted: hsla(0, 0%, 100%, 0.1); /* Muted/Subtle Background */
--cs-accent: hsla(0, 0%, 100%, 0.1); /* Accent Background */
--cs-ghost-hover: hsla(0, 0%, 100%, 0.1); /* Ghost Button Hover */
--cs-ghost-active: hsla(0, 0%, 100%, 0.15); /* Ghost Button Active */
/* Sidebar */
--cs-sidebar: var(--cs-black);
@ -558,4 +558,3 @@
--cs-ghost-button-background-hover: var(--cs-ghost-hover);
--cs-ghost-button-background-active: var(--cs-ghost-active);
}

View File

@ -8,7 +8,7 @@
@import 'tailwindcss';
@import 'tw-animate-css';
@import './theme.css'; /* 已包含 design-tokens.css 和所有 cs-* 工具类 */
@import './theme.css'; /* 已包含 design-tokens.css 和所有 cs-* 工具类 */
@custom-variant dark (&:is(.dark *));

View File

@ -239,4 +239,3 @@
--border-width-cs-md: var(--cs-border-width-md);
--border-width-cs-lg: var(--cs-border-width-lg);
}

View File

@ -1,7 +1,8 @@
import '@renderer/databases'
import { Spinner } from '@heroui/react'
import type { FC } from 'react'
import { useMemo } from 'react'
import { lazy, Suspense, useMemo } from 'react'
import { HashRouter, Route, Routes } from 'react-router-dom'
import Sidebar from './components/app/Sidebar'
@ -9,18 +10,25 @@ import { ErrorBoundary } from './components/ErrorBoundary'
import TabsContainer from './components/Tab/TabContainer'
import NavigationHandler from './handler/NavigationHandler'
import { useNavbarPosition } from './hooks/useNavbar'
import CodeToolsPage from './pages/code/CodeToolsPage'
import FilesPage from './pages/files/FilesPage'
import HomePage from './pages/home/HomePage'
import KnowledgePage from './pages/knowledge/KnowledgePage'
import LaunchpadPage from './pages/launchpad/LaunchpadPage'
import MinAppPage from './pages/minapps/MinAppPage'
import MinAppsPage from './pages/minapps/MinAppsPage'
import NotesPage from './pages/notes/NotesPage'
import PaintingsRoutePage from './pages/paintings/PaintingsRoutePage'
import SettingsPage from './pages/settings/SettingsPage'
import AssistantPresetsPage from './pages/store/assistants/presets/AssistantPresetsPage'
import TranslatePage from './pages/translate/TranslatePage'
const HomePage = lazy(() => import('./pages/home/HomePage'))
const AssistantPresetsPage = lazy(() => import('./pages/store/assistants/presets/AssistantPresetsPage'))
const PaintingsRoutePage = lazy(() => import('./pages/paintings/PaintingsRoutePage'))
const TranslatePage = lazy(() => import('./pages/translate/TranslatePage'))
const FilesPage = lazy(() => import('./pages/files/FilesPage'))
const NotesPage = lazy(() => import('./pages/notes/NotesPage'))
const KnowledgePage = lazy(() => import('./pages/knowledge/KnowledgePage'))
const MinAppPage = lazy(() => import('./pages/minapps/MinAppPage'))
const MinAppsPage = lazy(() => import('./pages/minapps/MinAppsPage'))
const CodeToolsPage = lazy(() => import('./pages/code/CodeToolsPage'))
const SettingsPage = lazy(() => import('./pages/settings/SettingsPage'))
const LaunchpadPage = lazy(() => import('./pages/launchpad/LaunchpadPage'))
const RouterFallback: FC = () => (
<div className="flex h-full w-full items-center justify-center">
<Spinner color="primary" size="lg" label="Loading" />
</div>
)
const Router: FC = () => {
const { navbarPosition } = useNavbarPosition()
@ -28,20 +36,22 @@ const Router: FC = () => {
const routes = useMemo(() => {
return (
<ErrorBoundary>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/store" element={<AssistantPresetsPage />} />
<Route path="/paintings/*" element={<PaintingsRoutePage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/files" element={<FilesPage />} />
<Route path="/notes" element={<NotesPage />} />
<Route path="/knowledge" element={<KnowledgePage />} />
<Route path="/apps/:appId" element={<MinAppPage />} />
<Route path="/apps" element={<MinAppsPage />} />
<Route path="/code" element={<CodeToolsPage />} />
<Route path="/settings/*" element={<SettingsPage />} />
<Route path="/launchpad" element={<LaunchpadPage />} />
</Routes>
<Suspense fallback={<RouterFallback />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/store" element={<AssistantPresetsPage />} />
<Route path="/paintings/*" element={<PaintingsRoutePage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/files" element={<FilesPage />} />
<Route path="/notes" element={<NotesPage />} />
<Route path="/knowledge" element={<KnowledgePage />} />
<Route path="/apps/:appId" element={<MinAppPage />} />
<Route path="/apps" element={<MinAppsPage />} />
<Route path="/code" element={<CodeToolsPage />} />
<Route path="/settings/*" element={<SettingsPage />} />
<Route path="/launchpad" element={<LaunchpadPage />} />
</Routes>
</Suspense>
</ErrorBoundary>
)
}, [])

View File

@ -20,6 +20,7 @@ export interface EditableNumberProps {
suffix?: string
prefix?: string
align?: 'start' | 'center' | 'end'
formatter?: (value: number | null) => string | number
}
const EditableNumber: FC<EditableNumberProps> = ({
@ -36,7 +37,8 @@ const EditableNumber: FC<EditableNumberProps> = ({
style,
className,
size = 'middle',
align = 'end'
align = 'end',
formatter
}) => {
const [isEditing, setIsEditing] = useState(false)
const [inputValue, setInputValue] = useState(value)
@ -90,7 +92,7 @@ const EditableNumber: FC<EditableNumberProps> = ({
changeOnBlur={changeOnBlur}
/>
<DisplayText style={style} className={className} $align={align} $isEditing={isEditing}>
{value ?? placeholder}
{formatter ? formatter(value ?? null) : (value ?? placeholder)}
</DisplayText>
</Container>
)

View File

@ -838,7 +838,7 @@
"label": "Context",
"tip": "The number of previous messages to keep in the context."
},
"max": "Max",
"max": "Unlimited",
"max_tokens": {
"confirm": "Set max tokens",
"confirm_content": "Set the maximum number of tokens the model can generate. Need to consider the context limit of the model, otherwise an error will be reported",
@ -1051,6 +1051,7 @@
"copied": "Copied",
"copy": "Copy",
"copy_failed": "Copy failed",
"current": "Current",
"cut": "Cut",
"default": "Default",
"delete": "Delete",

View File

@ -1051,6 +1051,7 @@
"copied": "已复制",
"copy": "复制",
"copy_failed": "复制失败",
"current": "当前",
"cut": "剪切",
"default": "默认",
"delete": "删除",

View File

@ -838,7 +838,7 @@
"label": "上下文",
"tip": "在上下文中保留的前幾則訊息"
},
"max": "最大",
"max": "不限",
"max_tokens": {
"confirm": "設置最大 Token 數",
"confirm_content": "設置單次交互所用的最大 Token 數,會影響返回結果的長度。要根據模型上下文限制來設定,否則會發生錯誤",
@ -1051,6 +1051,7 @@
"copied": "已複製",
"copy": "複製",
"copy_failed": "複製失敗",
"current": "当前",
"cut": "剪下",
"default": "預設",
"delete": "刪除",

View File

@ -1051,6 +1051,7 @@
"copied": "Kopiert",
"copy": "Kopieren",
"copy_failed": "Kopieren fehlgeschlagen",
"current": "Aktuell",
"cut": "Ausschneiden",
"default": "Standard",
"delete": "Löschen",

View File

@ -838,7 +838,7 @@
"label": "Πλήθος ενδιάμεσων",
"tip": "Πλήθος των μηνυμάτων που θα παραμείνουν στα ενδιάμεσα, όσο μεγαλύτερο είναι το αριθμός, τόσο μεγαλύτερο είναι το μήκος του ενδιάμεσου και τόσο περισσότερα tokens χρησιμοποιούνται. Συνομιλία συνήθως συνιστάται μεταξύ 5-10"
},
"max": "Όχι ορισμένο",
"max": "άπειρος",
"max_tokens": {
"confirm": "Ενεργοποίηση περιορισμού μήκους μηνύματος",
"confirm_content": "Μετά την ενεργοποίηση του περιορισμού μήκους μηνύματος, ο μέγιστος αριθμός των tokens που χρησιμοποιούνται κάθε φορά, θα επηρεάζει το μήκος της απάντησης. Πρέπει να το ρυθμίζετε βάσει των περιορισμών του πλαισίου του μοντέλου, διαφορετικά θα σφάλλεται.",
@ -1051,6 +1051,7 @@
"copied": "Αντιγράφηκε",
"copy": "Αντιγραφή",
"copy_failed": "Αποτυχία αντιγραφής",
"current": "Τρέχων",
"cut": "Κοπή",
"default": "Προεπιλογή",
"delete": "Διαγραφή",

View File

@ -1051,6 +1051,7 @@
"copied": "Copiado",
"copy": "Copiar",
"copy_failed": "Error al copiar",
"current": "Actual",
"cut": "Cortar",
"default": "Predeterminado",
"delete": "Eliminar",

View File

@ -1051,6 +1051,7 @@
"copied": "Copié",
"copy": "Copier",
"copy_failed": "Échec de la copie",
"current": "Actuel",
"cut": "Couper",
"default": "Défaut",
"delete": "Supprimer",

View File

@ -838,7 +838,7 @@
"label": "コンテキスト",
"tip": "コンテキストに保持する以前のメッセージの数"
},
"max": "最大",
"max": "制限なし",
"max_tokens": {
"confirm": "最大トークン数",
"confirm_content": "最大トークン数を設定すると、モデルが生成できる最大トークン数が制限されます。これにより、返される結果の長さに影響が出る可能性があります。モデルのコンテキスト制限に基づいて設定する必要があります。そうしないとエラーが発生します",
@ -1051,6 +1051,7 @@
"copied": "コピーされました",
"copy": "コピー",
"copy_failed": "コピーに失敗しました",
"current": "現在",
"cut": "切り取り",
"default": "デフォルト",
"delete": "削除",

View File

@ -1051,6 +1051,7 @@
"copied": "Copiado",
"copy": "Copiar",
"copy_failed": "Falha ao copiar",
"current": "Atual",
"cut": "Cortar",
"default": "Padrão",
"delete": "Excluir",

View File

@ -838,7 +838,7 @@
"label": "Контекст",
"tip": "Количество предыдущих сообщений, которые нужно сохранить в контексте."
},
"max": "Максимум",
"max": "без ограничений",
"max_tokens": {
"confirm": "Максимальное количество токенов",
"confirm_content": "Установить максимальное количество токенов, влияет на длину результата. Нужно учитывать контекст модели, иначе будет ошибка",
@ -1051,6 +1051,7 @@
"copied": "Скопировано",
"copy": "Копировать",
"copy_failed": "Не удалось скопировать",
"current": "Текущий",
"cut": "Вырезать",
"default": "По умолчанию",
"delete": "Удалить",

View File

@ -6,7 +6,7 @@ import type { GlobToolInput as GlobToolInputType, GlobToolOutput as GlobToolOutp
export function GlobTool({ input, output }: { input: GlobToolInputType; output?: GlobToolOutputType }) {
// 如果有输出,计算文件数量
const fileCount = output ? output.split('\n').filter((line) => line.trim()).length : 0
const lineCount = output ? output.split('\n').filter((line) => line.trim()).length : 0
return (
<AccordionItem
@ -17,7 +17,7 @@ export function GlobTool({ input, output }: { input: GlobToolInputType; output?:
icon={<FolderSearch className="h-4 w-4" />}
label="Glob"
params={input.pattern}
stats={output ? `${fileCount} found` : undefined}
stats={output ? `${lineCount} of output` : undefined}
/>
}>
<div>{output}</div>

View File

@ -8,20 +8,31 @@ import type { ReadToolInput as ReadToolInputType, ReadToolOutput as ReadToolOutp
import { AgentToolsType } from './types'
export function ReadTool({ input, output }: { input: ReadToolInputType; output?: ReadToolOutputType }) {
// 移除 system-reminder 标签及其内容的辅助函数
const removeSystemReminderTags = (text: string): string => {
// 使用正则表达式匹配 <system-reminder> 标签及其内容,包括换行符
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, '')
}
// 将 output 统一转换为字符串
const outputString = useMemo(() => {
if (!output) return null
let processedOutput: string
// 如果是 TextOutput[] 类型,提取所有 text 内容
if (Array.isArray(output)) {
return output
processedOutput = output
.filter((item): item is TextOutput => item.type === 'text')
.map((item) => item.text)
.map((item) => removeSystemReminderTags(item.text))
.join('')
} else {
// 如果是字符串,直接使用
processedOutput = output
}
// 如果是字符串,直接返回
return output
// 移除 system-reminder 标签及其内容
return removeSystemReminderTags(processedOutput)
}, [output])
// 如果有输出,计算统计信息

View File

@ -2,11 +2,7 @@ import { AccordionItem, Card, CardBody, Chip } from '@heroui/react'
import { CheckCircle, Circle, Clock, ListTodo } from 'lucide-react'
import { ToolTitle } from './GenericTools'
import type {
TodoItem,
TodoWriteToolInput as TodoWriteToolInputType,
TodoWriteToolOutput as TodoWriteToolOutputType
} from './types'
import type { TodoItem, TodoWriteToolInput as TodoWriteToolInputType } from './types'
import { AgentToolsType } from './types'
const getStatusConfig = (status: TodoItem['status']) => {
@ -34,7 +30,7 @@ const getStatusConfig = (status: TodoItem['status']) => {
}
}
export function TodoWriteTool({ input, output }: { input: TodoWriteToolInputType; output?: TodoWriteToolOutputType }) {
export function TodoWriteTool({ input }: { input: TodoWriteToolInputType }) {
const doneCount = input.todos.filter((todo) => todo.status === 'completed').length
return (
<AccordionItem
@ -72,7 +68,6 @@ export function TodoWriteTool({ input, output }: { input: TodoWriteToolInputType
)
})}
</div>
{output}
</AccordionItem>
)
}

View File

@ -2,7 +2,12 @@ import { Button, DescriptionSwitch, HelpTooltip, RowFlex, Selector, type Selecto
import { useMultiplePreferences, usePreference } from '@data/hooks/usePreference'
import EditableNumber from '@renderer/components/EditableNumber'
import Scrollbar from '@renderer/components/Scrollbar'
import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import {
DEFAULT_CONTEXTCOUNT,
DEFAULT_MAX_TOKENS,
DEFAULT_TEMPERATURE,
MAX_CONTEXT_COUNT
} from '@renderer/config/constant'
import { isOpenAIModel } from '@renderer/config/models'
import { UNKNOWN } from '@renderer/config/translate'
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
@ -214,9 +219,6 @@ const SettingsTab: FC<Props> = (props) => {
setStreamOutput(assistant?.settings?.streamOutput ?? true)
}, [assistant])
const assistantContextCount = assistant?.settings?.contextCount || 20
const maxContextCount = assistantContextCount > 20 ? assistantContextCount : 20
const model = assistant.model || getDefaultModel()
const isOpenAI = isOpenAIModel(model)
@ -269,21 +271,44 @@ const SettingsTab: FC<Props> = (props) => {
) : (
<SettingDivider />
)}
<Row align="middle">
<Row align="middle" gutter={10} justify="space-between">
<SettingRowTitleSmall>
{t('chat.settings.context_count.label')}
<HelpTooltip title={t('chat.settings.context_count.tip')} />
</SettingRowTitleSmall>
<Col span={8}>
<EditableNumber
min={0}
max={20}
step={1}
value={contextCount}
changeOnBlur
onChange={(value) => {
if (value !== null && value >= 0 && value <= 20) {
setContextCount(value)
onContextCountChange(value)
}
}}
formatter={(value) => (value === MAX_CONTEXT_COUNT ? t('chat.settings.max') : (value ?? ''))}
style={{ width: '100%' }}
/>
</Col>
</Row>
<Row align="middle" gutter={10}>
<Col span={23}>
<Col span={24}>
<Slider
min={0}
max={maxContextCount}
max={20}
onChange={setContextCount}
onChangeComplete={onContextCountChange}
value={typeof contextCount === 'number' ? contextCount : 0}
value={Math.min(contextCount, 20)}
tooltip={{ open: false }}
step={1}
marks={{
0: '0',
10: '10',
20: '20'
}}
/>
</Col>
</Row>

View File

@ -360,6 +360,7 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
setTimeoutTimer('contextCount_onChange', () => updateAssistantSettings({ contextCount: value }), 500)
}
}}
formatter={(value) => (value === MAX_CONTEXT_COUNT ? t('chat.settings.max') : (value ?? ''))}
style={{ width: '100%' }}
/>
</Col>
@ -374,7 +375,7 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
value={typeof contextCount === 'number' ? contextCount : 0}
marks={{ 0: '0', 25: '25', 50: '50', 75: '75', 100: t('chat.settings.max') }}
step={1}
tooltip={{ formatter: formatSliderTooltip }}
tooltip={{ formatter: formatSliderTooltip, open: false }}
/>
</Col>
</Row>