From 42f54858996ece323beb54e8ad07892a4b042306 Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat <43230886+MyPrototypeWhat@users.noreply.github.com> Date: Sat, 17 May 2025 23:53:57 +0800 Subject: [PATCH 1/7] fix: group message resend (#6106) --- src/renderer/src/store/thunk/messageThunk.ts | 40 +++++++++++++------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/store/thunk/messageThunk.ts b/src/renderer/src/store/thunk/messageThunk.ts index c2c51b25e4..78f2876212 100644 --- a/src/renderer/src/store/thunk/messageThunk.ts +++ b/src/renderer/src/store/thunk/messageThunk.ts @@ -805,21 +805,19 @@ export const deleteMessageGroupThunk = const currentState = getState() const topicMessageIds = currentState.messages.messageIdsByTopic[topicId] || [] const messagesToDelete: Message[] = [] - const idsToDelete: string[] = [] topicMessageIds.forEach((id) => { const msg = currentState.messages.entities[id] if (msg && msg.askId === askId) { messagesToDelete.push(msg) - idsToDelete.push(id) } }) - const userQuery = currentState.messages.entities[askId] - if (userQuery && userQuery.topicId === topicId && !idsToDelete.includes(askId)) { - messagesToDelete.push(userQuery) - idsToDelete.push(askId) - } + // const userQuery = currentState.messages.entities[askId] + // if (userQuery && userQuery.topicId === topicId && !idsToDelete.includes(askId)) { + // messagesToDelete.push(userQuery) + // idsToDelete.push(askId) + // } if (messagesToDelete.length === 0) { console.warn(`[deleteMessageGroup] No messages found with askId ${askId} in topic ${topicId}.`) @@ -894,13 +892,29 @@ export const resendMessageThunk = const resetDataList: Message[] = [] if (assistantMessagesToReset.length === 0) { - // 没有用户消息,就创建一个 - const assistantMessage = createAssistantMessage(assistant.id, topicId, { - askId: userMessageToResend.id, - model: assistant.model + // 没有用户消息,就创建一个或多个 + + if (userMessageToResend?.mentions?.length) { + console.log('userMessageToResend.mentions', userMessageToResend.mentions) + for (const mention of userMessageToResend.mentions) { + const assistantMessage = createAssistantMessage(assistant.id, topicId, { + askId: userMessageToResend.id, + model: mention, + modelId: mention.id + }) + resetDataList.push(assistantMessage) + } + } else { + const assistantMessage = createAssistantMessage(assistant.id, topicId, { + askId: userMessageToResend.id, + model: assistant.model + }) + resetDataList.push(assistantMessage) + } + + resetDataList.forEach((message) => { + dispatch(newMessagesActions.addMessage({ topicId, message })) }) - resetDataList.push(assistantMessage) - dispatch(newMessagesActions.addMessage({ topicId, message: assistantMessage })) } const allBlockIdsToDelete: string[] = [] From f6a496d1b99d07d9c39c40e2d44cddf810f16fc9 Mon Sep 17 00:00:00 2001 From: one Date: Sun, 18 May 2025 10:05:19 +0800 Subject: [PATCH 2/7] refactor: clean up code for `MessageGroupModelList` (#6084) * refactor: clean up comments and useless z-index * refactor: remove useless styles, restore segment animation, extract renderLabel * refactor: add left margin to the toggle button * revert: font size --- .../home/Messages/MessageGroupModelList.tsx | 135 +++++++----------- 1 file changed, 49 insertions(+), 86 deletions(-) diff --git a/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx b/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx index 328e25159c..8fe085aa2f 100644 --- a/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx +++ b/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx @@ -1,5 +1,6 @@ import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' +import { HStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' import { useSettings } from '@renderer/hooks/useSettings' import { useAppDispatch } from '@renderer/store' @@ -7,7 +8,7 @@ import { setFoldDisplayMode } from '@renderer/store/settings' import type { Model } from '@renderer/types' import type { Message } from '@renderer/types/newMessage' import { Avatar, Segmented as AntdSegmented, Tooltip } from 'antd' -import { FC } from 'react' +import { FC, memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -25,39 +26,54 @@ const MessageGroupModelList: FC = ({ messages, selec const { foldDisplayMode } = useSettings() const isCompact = foldDisplayMode === 'compact' + const renderLabel = useCallback( + (message: Message) => { + const modelTip = message.model?.name + + if (isCompact) { + return ( + + { + setSelectedMessage(message) + }}> + + + + ) + } + return ( + + + {message.model?.name} + + ) + }, + [isCompact, selectMessageId, setSelectedMessage] + ) + return ( - + dispatch(setFoldDisplayMode(isCompact ? 'expanded' : 'compact'))}> - {foldDisplayMode === 'compact' ? : } + {isCompact ? : } - {foldDisplayMode === 'compact' ? ( + {isCompact ? ( /* Compact style display */ - - {messages.map((message, index) => ( - - { - setSelectedMessage(message) - }}> - - - - ))} - + {messages.map((message) => renderLabel(message))} ) : ( /* Expanded style display */ = ({ messages, selec setSelectedMessage(message) }} options={messages.map((message) => ({ - label: ( - - - {message.model?.name} - - ), + label: renderLabel(message), value: message.id }))} size="small" /> )} - + ) } -const ModelsWrapper = styled.div` - position: relative; - display: flex; +const Container = styled(HStack)` flex: 1; overflow: hidden; + align-items: center; + margin-left: 4px; ` const DisplayModeToggle = styled.div<{ displayMode: DisplayMode }>` - position: absolute; - left: 4px; /* Add more space on the left */ - top: 50%; - transform: translateY(-50%); - z-index: 5; - width: 28px; /* Increase width */ - height: 28px; /* Add height */ display: flex; - justify-content: center; - align-items: center; cursor: pointer; + padding: 2px 6px 3px 6px; border-radius: 4px; - padding: 2px; + width: 26px; + height: 26px; - /* Add hover effect */ &:hover { background-color: var(--color-hover); } @@ -119,9 +122,7 @@ const ModelsContainer = styled(Scrollbar)<{ $displayMode: DisplayMode }>` overflow-x: auto; flex: 1; padding: 0 8px; - margin-left: 24px; /* Space for toggle button */ - /* Hide scrollbar to match original code */ &::-webkit-scrollbar { display: none; } @@ -131,27 +132,23 @@ const ModelsContainer = styled(Scrollbar)<{ $displayMode: DisplayMode }>` display: flex; align-items: center; flex-wrap: nowrap; - position: relative; padding: 6px 4px; /* Base style - default overlapping effect */ & > * { margin-left: -6px !important; - /* Separate transition properties to avoid conflicts */ transition: transform 0.18s ease-out, margin 0.18s ease-out !important; position: relative; - /* Only use will-change for transform to reduce rendering overhead */ will-change: transform; } - /* First element has no left margin */ & > *:first-child { margin-left: 0 !important; } - /* Using :has() selector to handle the element before the hovered one */ + /* Element before the hovered one */ & > *:has(+ *:hover) { margin-right: 2px !important; /* Use transform instead of margin to reduce layout recalculations */ @@ -171,52 +168,24 @@ const ModelsContainer = styled(Scrollbar)<{ $displayMode: DisplayMode }>` } ` -const AvatarWrapper = styled.div<{ isSelected: boolean }>` +const AvatarWrapper = styled.div<{ $isSelected: boolean }>` cursor: pointer; display: inline-flex; border-radius: 50%; - /* Keep z-index separate from transitions to avoid rendering issues */ - z-index: ${(props) => (props.isSelected ? 2 : 0)}; background: var(--color-background); - /* Simplify transitions to reduce jittering */ transition: transform 0.18s ease-out, margin 0.18s ease-out, - box-shadow 0.18s ease-out, filter 0.18s ease-out; - box-shadow: 0 0 0 1px var(--color-background); - - /* Use CSS variables to define animation parameters for easy adjustment */ - --hover-scale: 1.15; - --hover-x-offset: 6px; - --hover-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + z-index: ${(props) => (props.$isSelected ? 1 : 0)}; + border: ${(props) => (props.$isSelected ? '2px solid var(--color-primary)' : 'none')}; &:hover { - /* z-index is applied immediately, not part of the transition */ - z-index: 10; - transform: translateX(var(--hover-x-offset)) scale(var(--hover-scale)); - box-shadow: var(--hover-shadow); + transform: translateX(6px) scale(1.15); filter: brightness(1.02); margin-left: 8px !important; margin-right: 4px !important; } - - ${(props) => - props.isSelected && - ` - border: 2px solid var(--color-primary); - z-index: 2; - - &:hover { - /* z-index is applied immediately, not part of the transition */ - z-index: 10; - border: 2px solid var(--color-primary); - filter: brightness(1.02); - transform: translateX(var(--hover-x-offset)) scale(var(--hover-scale)); - margin-left: 8px !important; - margin-right: 4px !important; - } - `} ` const Segmented = styled(AntdSegmented)` @@ -224,21 +193,15 @@ const Segmented = styled(AntdSegmented)` background-color: transparent !important; .ant-segmented-item { - background-color: transparent !important; - transition: none !important; border-radius: var(--list-item-border-radius) !important; - box-shadow: none !important; &:hover { background: transparent !important; } } .ant-segmented-thumb, .ant-segmented-item-selected { - background-color: transparent !important; border: 0.5px solid var(--color-border); - transition: none !important; border-radius: var(--list-item-border-radius) !important; - box-shadow: none !important; } ` @@ -254,4 +217,4 @@ const ModelName = styled.span` font-size: 12px; ` -export default MessageGroupModelList +export default memo(MessageGroupModelList) From bd65e7bac4d2493eabfa9fb41277f6613134f787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=AA=E7=94=B1=E7=9A=84=E4=B8=96=E7=95=8C=E4=BA=BA?= <3196812536@qq.com> Date: Sun, 18 May 2025 10:11:14 +0800 Subject: [PATCH 3/7] feat: make sidebar setting group collapsible (#6019) * feat: code tools, editor, executor CodeEditor & Preview - CodeEditor: CodeMirror 6 - Switch to CodeEditor in the settings - Support edit&save with a accurate diff&lookup strategy - Use CodeEditor for editing MCP json configuration - CodePreview: Original Shiki syntax highlighting - Implemented using a custom Shiki stream tokenizer - Remov code caching as it is incompatible with the current streaming code highlighting - Add a webworker for shiki - Other preview components - Merge MermaidPopup and Mermaid to MermaidPreview, use local mermaidjs - Show mermaid syntax error message on demand - Rename PlantUML to PlantUmlPreview - Rename SyntaxHighlighterProvider to CodeStyleProvider for clarity - Both light and dark themes are preserved for convenience CodeToolbar - Top sticky toolbar provides quick tools (left) and core tools (right) - Quick tools are hidden under the `More` button to avoid clutter, while core tools are always visible - View&edit mode - Allow switching between preview and edit modes - Add a split view Code execution - Pyodide for executing Python scripts - Add a webworker for Pyodide * fix: migrate version and lint error * refactor: use constants for defining tool specs * feat: make setting group collapsible * fix: yarn.lock * fix: conflict error --------- Co-authored-by: one --- .../src/pages/home/Tabs/SettingsTab.tsx | 922 +++++++++--------- .../src/pages/settings/SettingGroup.tsx | 62 ++ 2 files changed, 527 insertions(+), 457 deletions(-) create mode 100644 src/renderer/src/pages/settings/SettingGroup.tsx diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index 43a439a1ae..2270566bbb 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -11,8 +11,9 @@ import { import { useCodeStyle } from '@renderer/context/CodeStyleProvider' import { useAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' -import { SettingDivider, SettingRow, SettingRowTitle, SettingSubtitle } from '@renderer/pages/settings' +import { SettingDivider, SettingRow, SettingRowTitle } from '@renderer/pages/settings' import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' +import { CollapsibleSettingGroup } from '@renderer/pages/settings/SettingGroup' import { useAppDispatch } from '@renderer/store' import { SendMessageShortcut, @@ -49,7 +50,7 @@ import { TranslateLanguageVarious } from '@renderer/types' import { modalConfirm } from '@renderer/utils' -import { Button, Col, InputNumber, Row, Select, Slider, Switch, Tooltip } from 'antd' +import { Button, Col, Divider, InputNumber, Row, Select, Slider, Switch, Tooltip } from 'antd' import { CircleHelp, RotateCcw, Settings2 } from 'lucide-react' import { FC, useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -181,478 +182,485 @@ const SettingsTab: FC = (props) => { return ( - - + - {t('assistants.settings.title')}{' '} +