mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 02:20:10 +08:00
feat: Add a new grid mode for message display. (#1626)
* chore(version): 0.9.23 * feat(renderer): 新增网格模式的消息展示方式 * feat(message): 新增消息网格展示相关设置 * 根据 gridPopoverTrigger 属性动态设置消息分组的样式 * 在 MessageMenubar 组件中,各个按钮 click 事件阻止事件冒泡,避免打开 popover * 多模型回答样式添加网格模式并优化消息样式 --------- Co-authored-by: kangfenmao <kangfenmao@qq.com>
This commit is contained in:
parent
e3115d00bf
commit
bad2f15c1f
@ -364,6 +364,7 @@
|
||||
"message.multi_model_style.fold": "Fold",
|
||||
"message.multi_model_style.horizontal": "Horizontal",
|
||||
"message.multi_model_style.vertical": "Vertical",
|
||||
"message.multi_model_style.grid": "Grid",
|
||||
"message.style": "Message style",
|
||||
"message.style.bubble": "Bubble",
|
||||
"message.style.plain": "Plain",
|
||||
@ -636,6 +637,8 @@
|
||||
"messages.input.title": "Input Settings",
|
||||
"messages.markdown_rendering_input_message": "Markdown render input message",
|
||||
"messages.math_engine": "Math engine",
|
||||
"messages.grid_columns": "Message grid display columns",
|
||||
"messages.grid_popover_trigger": "Grid detail trigger",
|
||||
"messages.metrics": "{{time_first_token_millsec}}ms to first token | {{token_speed}} tok/sec",
|
||||
"messages.model.title": "Model Settings",
|
||||
"messages.title": "Message Settings",
|
||||
|
||||
@ -363,6 +363,7 @@
|
||||
"message.multi_model_style.fold": "折りたたむ",
|
||||
"message.multi_model_style.horizontal": "水平",
|
||||
"message.multi_model_style.vertical": "垂直",
|
||||
"message.multi_model_style.grid": "グリッド",
|
||||
"message.style": "メッセージスタイル",
|
||||
"message.style.bubble": "バブル",
|
||||
"message.style.plain": "プレーン",
|
||||
@ -636,6 +637,8 @@
|
||||
"messages.input.title": "入力設定",
|
||||
"messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
||||
"messages.math_engine": "数式エンジン",
|
||||
"messages.grid_columns": "メッセージグリッドの表示列数",
|
||||
"messages.grid_popover_trigger": "グリッド詳細トリガー",
|
||||
"messages.metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec",
|
||||
"messages.model.title": "モデル設定",
|
||||
"messages.title": "メッセージ設定",
|
||||
|
||||
@ -364,6 +364,7 @@
|
||||
"message.multi_model_style.fold": "Свернуть",
|
||||
"message.multi_model_style.horizontal": "Горизонтальный",
|
||||
"message.multi_model_style.vertical": "Вертикальный",
|
||||
"message.multi_model_style.grid": "клетчатый вид",
|
||||
"message.style": "Стиль сообщения",
|
||||
"message.style.bubble": "Пузырь",
|
||||
"message.style.plain": "Простой",
|
||||
@ -637,6 +638,8 @@
|
||||
"messages.math_engine": "Математический движок",
|
||||
"messages.metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec",
|
||||
"messages.model.title": "Настройки модели",
|
||||
"messages.grid_columns": "Количество столбцов сетки сообщений",
|
||||
"messages.grid_popover_trigger": "Триггер для отображения подробной информации в сетке",
|
||||
"messages.title": "Настройки сообщений",
|
||||
"messages.use_serif_font": "Использовать serif шрифт",
|
||||
"model": "Модель по умолчанию",
|
||||
|
||||
@ -366,6 +366,7 @@
|
||||
"message.multi_model_style.fold": "折叠",
|
||||
"message.multi_model_style.horizontal": "水平",
|
||||
"message.multi_model_style.vertical": "垂直",
|
||||
"message.multi_model_style.grid": "网格",
|
||||
"message.style": "消息样式",
|
||||
"message.style.bubble": "气泡",
|
||||
"message.style.plain": "简洁",
|
||||
@ -636,6 +637,8 @@
|
||||
"messages.input.title": "输入设置",
|
||||
"messages.markdown_rendering_input_message": "Markdown 渲染输入消息",
|
||||
"messages.math_engine": "数学公式引擎",
|
||||
"messages.grid_columns": "消息网格展示列数",
|
||||
"messages.grid_popover_trigger": "网格详情触发",
|
||||
"messages.metrics": "首字时延 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
||||
"messages.model.title": "模型设置",
|
||||
"messages.title": "消息设置",
|
||||
|
||||
@ -364,6 +364,7 @@
|
||||
"message.multi_model_style.fold": "折疊",
|
||||
"message.multi_model_style.horizontal": "水平",
|
||||
"message.multi_model_style.vertical": "垂直",
|
||||
"message.multi_model_style.grid": "网格",
|
||||
"message.style": "消息樣式",
|
||||
"message.style.bubble": "氣泡",
|
||||
"message.style.plain": "簡潔",
|
||||
@ -635,6 +636,8 @@
|
||||
"messages.input.show_estimated_tokens": "顯示預估 Token 數",
|
||||
"messages.input.title": "輸入設定",
|
||||
"messages.math_engine": "Markdown 渲染輸入訊息",
|
||||
"messages.grid_columns": "消息網格展示列數",
|
||||
"messages.grid_popover_trigger": "網格詳情觸發",
|
||||
"messages.metrics": "首字時延 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
||||
"messages.model.title": "模型設定",
|
||||
"messages.title": "訊息設定",
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { ColumnHeightOutlined, ColumnWidthOutlined, DeleteOutlined, FolderOutlined } from '@ant-design/icons'
|
||||
import {
|
||||
ColumnHeightOutlined,
|
||||
ColumnWidthOutlined,
|
||||
DeleteOutlined,
|
||||
FolderOutlined,
|
||||
NumberOutlined
|
||||
} from '@ant-design/icons'
|
||||
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
@ -6,7 +12,7 @@ import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||
import { MultiModelMessageStyle } from '@renderer/store/settings'
|
||||
import { Message, Model, Topic } from '@renderer/types'
|
||||
import { Button, Segmented as AntdSegmented } from 'antd'
|
||||
import { Button, Popover, Segmented as AntdSegmented } from 'antd'
|
||||
import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled, { css } from 'styled-components'
|
||||
@ -32,7 +38,7 @@ const MessageGroup: FC<Props> = ({
|
||||
onGetMessages,
|
||||
onDeleteGroupMessages
|
||||
}) => {
|
||||
const { multiModelMessageStyle: multiModelMessageStyleSetting } = useSettings()
|
||||
const { multiModelMessageStyle: multiModelMessageStyleSetting, gridColumns, gridPopoverTrigger } = useSettings()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [multiModelMessageStyle, setMultiModelMessageStyle] =
|
||||
@ -67,33 +73,89 @@ const MessageGroup: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<GroupContainer $isGrouped={isGrouped} $layout={multiModelMessageStyle}>
|
||||
<GridContainer $count={messageLength} $layout={multiModelMessageStyle}>
|
||||
{messages.map((message, index) => (
|
||||
<MessageWrapper
|
||||
$layout={multiModelMessageStyle}
|
||||
$selected={index === selectedIndex}
|
||||
$isGrouped={isGrouped}
|
||||
key={message.id}
|
||||
className={message.role === 'assistant' && isHorizontal && isGrouped ? 'group-message-wrapper' : ''}>
|
||||
<MessageItem
|
||||
isGrouped={isGrouped}
|
||||
message={message}
|
||||
topic={topic}
|
||||
index={message.index}
|
||||
hidePresetMessages={hidePresetMessages}
|
||||
style={{ paddingTop: isGrouped && multiModelMessageStyle === 'horizontal' ? 0 : 15 }}
|
||||
onSetMessages={onSetMessages}
|
||||
onDeleteMessage={onDeleteMessage}
|
||||
onGetMessages={onGetMessages}
|
||||
/>
|
||||
</MessageWrapper>
|
||||
))}
|
||||
<GridContainer $count={messageLength} $layout={multiModelMessageStyle} $gridColumns={gridColumns}>
|
||||
{messages.map((message, index) =>
|
||||
multiModelMessageStyle === 'grid' && message.role === 'assistant' && isGrouped ? (
|
||||
<Popover
|
||||
content={
|
||||
<MessageWrapper
|
||||
$layout={multiModelMessageStyle}
|
||||
$selected={index === selectedIndex}
|
||||
$isGrouped={isGrouped}
|
||||
$isInPopover={true}
|
||||
key={message.id}>
|
||||
<MessageItem
|
||||
isGrouped={isGrouped}
|
||||
message={message}
|
||||
topic={topic}
|
||||
index={message.index}
|
||||
hidePresetMessages={hidePresetMessages}
|
||||
style={{
|
||||
paddingTop: isGrouped && ['horizontal', 'grid'].includes(multiModelMessageStyle) ? 0 : 15
|
||||
}}
|
||||
onSetMessages={onSetMessages}
|
||||
onDeleteMessage={onDeleteMessage}
|
||||
onGetMessages={onGetMessages}
|
||||
/>
|
||||
</MessageWrapper>
|
||||
}
|
||||
trigger={gridPopoverTrigger}
|
||||
styles={{ root: { maxWidth: '60vw', minWidth: '550px', overflowY: 'auto', zIndex: 1000 } }}
|
||||
getPopupContainer={(triggerNode) => triggerNode.parentNode as HTMLElement}
|
||||
key={message.id}>
|
||||
<MessageWrapper
|
||||
$layout={multiModelMessageStyle}
|
||||
$selected={index === selectedIndex}
|
||||
$isGrouped={isGrouped}
|
||||
key={message.id}>
|
||||
<MessageItem
|
||||
isGrouped={isGrouped}
|
||||
message={message}
|
||||
topic={topic}
|
||||
index={message.index}
|
||||
hidePresetMessages={hidePresetMessages}
|
||||
style={
|
||||
gridPopoverTrigger === 'hover' && isGrouped
|
||||
? {
|
||||
paddingTop: isGrouped && ['horizontal', 'grid'].includes(multiModelMessageStyle) ? 0 : 15,
|
||||
overflow: isGrouped ? 'hidden' : 'auto',
|
||||
maxHeight: isGrouped ? '280px' : 'unset'
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onSetMessages={onSetMessages}
|
||||
onDeleteMessage={onDeleteMessage}
|
||||
onGetMessages={onGetMessages}
|
||||
/>
|
||||
</MessageWrapper>
|
||||
</Popover>
|
||||
) : (
|
||||
<MessageWrapper
|
||||
$layout={multiModelMessageStyle}
|
||||
$selected={index === selectedIndex}
|
||||
$isGrouped={isGrouped}
|
||||
key={message.id}
|
||||
className={message.role === 'assistant' && isHorizontal && isGrouped ? 'group-message-wrapper' : ''}>
|
||||
<MessageItem
|
||||
isGrouped={isGrouped}
|
||||
message={message}
|
||||
topic={topic}
|
||||
index={message.index}
|
||||
hidePresetMessages={hidePresetMessages}
|
||||
style={{ paddingTop: isGrouped && ['horizontal', 'grid'].includes(multiModelMessageStyle) ? 0 : 15 }}
|
||||
onSetMessages={onSetMessages}
|
||||
onDeleteMessage={onDeleteMessage}
|
||||
onGetMessages={onGetMessages}
|
||||
/>
|
||||
</MessageWrapper>
|
||||
)
|
||||
)}
|
||||
</GridContainer>
|
||||
{isGrouped && (
|
||||
<GroupMenuBar className="group-menu-bar" $layout={multiModelMessageStyle}>
|
||||
<HStack style={{ alignItems: 'center', flex: 1, overflow: 'hidden' }}>
|
||||
<LayoutContainer>
|
||||
{['fold', 'vertical', 'horizontal'].map((layout) => (
|
||||
{['fold', 'vertical', 'horizontal', 'grid'].map((layout) => (
|
||||
<LayoutOption
|
||||
key={layout}
|
||||
active={multiModelMessageStyle === layout}
|
||||
@ -102,8 +164,10 @@ const MessageGroup: FC<Props> = ({
|
||||
<FolderOutlined />
|
||||
) : layout === 'horizontal' ? (
|
||||
<ColumnWidthOutlined />
|
||||
) : (
|
||||
) : layout === 'vertical' ? (
|
||||
<ColumnHeightOutlined />
|
||||
) : (
|
||||
<NumberOutlined />
|
||||
)}
|
||||
</LayoutOption>
|
||||
))}
|
||||
@ -143,30 +207,38 @@ const MessageGroup: FC<Props> = ({
|
||||
}
|
||||
|
||||
const GroupContainer = styled.div<{ $isGrouped: boolean; $layout: MultiModelMessageStyle }>`
|
||||
padding-top: ${({ $isGrouped, $layout }) => ($isGrouped && $layout === 'horizontal' ? '15px' : '0')};
|
||||
padding-top: ${({ $isGrouped, $layout }) => ($isGrouped && 'horizontal' === $layout ? '15px' : '0')};
|
||||
`
|
||||
|
||||
const GridContainer = styled.div<{ $count: number; $layout: MultiModelMessageStyle }>`
|
||||
const GridContainer = styled.div<{ $count: number; $layout: MultiModelMessageStyle; $gridColumns: number }>`
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(
|
||||
${(props) => (['fold', 'vertical'].includes(props.$layout) ? 1 : props.$count)},
|
||||
${({ $layout, $count }) => (['fold', 'vertical'].includes($layout) ? 1 : $count)},
|
||||
minmax(550px, 1fr)
|
||||
);
|
||||
gap: ${({ $layout }) => ($layout === 'horizontal' ? '16px' : '0')};
|
||||
@media (max-width: 800px) {
|
||||
grid-template-columns: repeat(
|
||||
${(props) => (['fold', 'vertical'].includes(props.$layout) ? 1 : props.$count)},
|
||||
${({ $layout, $count }) => (['fold', 'vertical'].includes($layout) ? 1 : $count)},
|
||||
minmax(400px, 1fr)
|
||||
);
|
||||
}
|
||||
overflow-y: auto;
|
||||
${({ $gridColumns, $layout, $count }) =>
|
||||
$layout === 'grid' &&
|
||||
css`
|
||||
grid-template-columns: repeat(${$count > 1 ? $gridColumns || 2 : 1}, minmax(0, 1fr));
|
||||
grid-template-rows: auto;
|
||||
gap: 16px;
|
||||
`}
|
||||
`
|
||||
|
||||
interface MessageWrapperProps {
|
||||
$layout: 'fold' | 'horizontal' | 'vertical'
|
||||
$layout: 'fold' | 'horizontal' | 'vertical' | 'grid'
|
||||
$selected: boolean
|
||||
$isGrouped: boolean
|
||||
$isInPopover?: boolean
|
||||
}
|
||||
|
||||
const MessageWrapper = styled(Scrollbar)<MessageWrapperProps>`
|
||||
@ -193,6 +265,22 @@ const MessageWrapper = styled(Scrollbar)<MessageWrapperProps>`
|
||||
}
|
||||
return ''
|
||||
}}
|
||||
|
||||
${({ $layout, $isInPopover, $isGrouped }) =>
|
||||
$layout === 'grid' && $isGrouped
|
||||
? css`
|
||||
max-height: ${$isInPopover ? '50vh' : '300px'};
|
||||
overflow-y: auto;
|
||||
border: 0.5px solid var(--color-border);
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
`
|
||||
: css`
|
||||
overflow-y: auto;
|
||||
border: 0.5px solid transparent;
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
`}
|
||||
`
|
||||
|
||||
const GroupMenuBar = styled.div<{ $layout: MultiModelMessageStyle }>`
|
||||
|
||||
@ -60,12 +60,16 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
|
||||
const isUserMessage = message.role === 'user'
|
||||
|
||||
const onCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(removeTrailingDoubleSpaces(message.content))
|
||||
window.message.success({ content: t('message.copied'), key: 'copy-message' })
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}, [message.content, t])
|
||||
const onCopy = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
navigator.clipboard.writeText(removeTrailingDoubleSpaces(message.content))
|
||||
window.message.success({ content: t('message.copied'), key: 'copy-message' })
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
},
|
||||
[message.content, t]
|
||||
)
|
||||
|
||||
const onNewBranch = useCallback(async () => {
|
||||
await modelGenerating()
|
||||
@ -195,14 +199,16 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
[message, onEdit, onNewBranch, t]
|
||||
)
|
||||
|
||||
const onRegenerate = async () => {
|
||||
const onRegenerate = async (e: React.MouseEvent | undefined) => {
|
||||
e?.stopPropagation?.()
|
||||
await modelGenerating()
|
||||
const selectedModel = isGrouped ? model : assistantModel
|
||||
const _message = resetAssistantMessage(message, selectedModel)
|
||||
onEditMessage?.(_message)
|
||||
}
|
||||
|
||||
const onMentionModel = async () => {
|
||||
const onMentionModel = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
await modelGenerating()
|
||||
const selectedModel = await SelectModelPopup.show({ model })
|
||||
if (!selectedModel) return
|
||||
@ -216,9 +222,13 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
onEditMessage?.(_message)
|
||||
}
|
||||
|
||||
const onUseful = useCallback(() => {
|
||||
onEditMessage?.({ ...message, useful: !message.useful })
|
||||
}, [message, onEditMessage])
|
||||
const onUseful = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
onEditMessage?.({ ...message, useful: !message.useful })
|
||||
},
|
||||
[message, onEditMessage]
|
||||
)
|
||||
|
||||
return (
|
||||
<MenusBar className={`menubar ${isLastMessage && 'show'}`}>
|
||||
@ -270,13 +280,14 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
key: 'translate-close',
|
||||
onClick: () => onEditMessage?.({ ...message, translatedContent: undefined })
|
||||
}
|
||||
]
|
||||
],
|
||||
onClick: (e) => e.domEvent.stopPropagation()
|
||||
}}
|
||||
trigger={['click']}
|
||||
placement="topRight"
|
||||
arrow>
|
||||
<Tooltip title={t('chat.translate')} mouseEnterDelay={1.2}>
|
||||
<ActionButton className="message-action-button">
|
||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
||||
<TranslationOutlined />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
@ -298,14 +309,25 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
<Tooltip title={t('common.delete')} mouseEnterDelay={1}>
|
||||
<ActionButton
|
||||
className="message-action-button"
|
||||
onClick={isGrouped ? () => onDeleteMessage?.(message) : undefined}>
|
||||
onClick={
|
||||
isGrouped
|
||||
? (e) => {
|
||||
e.stopPropagation()
|
||||
onDeleteMessage?.(message)
|
||||
}
|
||||
: (e) => e.stopPropagation()
|
||||
}>
|
||||
<DeleteOutlined />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
</Popconfirm>
|
||||
{!isUserMessage && (
|
||||
<Dropdown menu={{ items: dropdownItems }} trigger={['click']} placement="topRight" arrow>
|
||||
<ActionButton className="message-action-button">
|
||||
<Dropdown
|
||||
menu={{ items: dropdownItems, onClick: (e) => e.domEvent.stopPropagation() }}
|
||||
trigger={['click']}
|
||||
placement="topRight"
|
||||
arrow>
|
||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
||||
<MenuOutlined />
|
||||
</ActionButton>
|
||||
</Dropdown>
|
||||
|
||||
@ -19,6 +19,8 @@ import {
|
||||
setCodeShowLineNumbers,
|
||||
setCodeStyle,
|
||||
setFontSize,
|
||||
setGridColumns,
|
||||
setGridPopoverTrigger,
|
||||
setMathEngine,
|
||||
setMessageFont,
|
||||
setMessageStyle,
|
||||
@ -42,13 +44,14 @@ interface Props {
|
||||
|
||||
const SettingsTab: FC<Props> = (props) => {
|
||||
const { assistant, updateAssistantSettings, updateAssistant } = useAssistant(props.assistant.id)
|
||||
const { messageStyle, codeStyle, fontSize, language } = useSettings()
|
||||
const { messageStyle, codeStyle, fontSize, language, gridColumns } = useSettings()
|
||||
|
||||
const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
|
||||
const [contextCount, setContextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
|
||||
const [enableMaxTokens, setEnableMaxTokens] = useState(assistant?.settings?.enableMaxTokens ?? false)
|
||||
const [maxTokens, setMaxTokens] = useState(assistant?.settings?.maxTokens ?? 0)
|
||||
const [fontSizeValue, setFontSizeValue] = useState(fontSize)
|
||||
const [gridColumnsValue, setGridColumnsValue] = useState(gridColumns)
|
||||
const [streamOutput, setStreamOutput] = useState(assistant?.settings?.streamOutput ?? true)
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -69,7 +72,8 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
mathEngine,
|
||||
autoTranslateWithSpace,
|
||||
pasteLongTextThreshold,
|
||||
multiModelMessageStyle
|
||||
multiModelMessageStyle,
|
||||
gridPopoverTrigger
|
||||
} = useSettings()
|
||||
|
||||
const onUpdateAssistantSettings = (settings: Partial<AssistantSettings>) => {
|
||||
@ -283,6 +287,7 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
<Select.Option value="fold">{t('message.message.multi_model_style.fold')}</Select.Option>
|
||||
<Select.Option value="vertical">{t('message.message.multi_model_style.vertical')}</Select.Option>
|
||||
<Select.Option value="horizontal">{t('message.message.multi_model_style.horizontal')}</Select.Option>
|
||||
<Select.Option value="grid">{t('message.message.multi_model_style.grid')}</Select.Option>
|
||||
</Select>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
@ -313,6 +318,34 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
</Select>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('settings.messages.grid_popover_trigger')}</SettingRowTitleSmall>
|
||||
<Select
|
||||
value={gridPopoverTrigger || 'hover'}
|
||||
onChange={(value) => dispatch(setGridPopoverTrigger(value))}
|
||||
style={{ width: 135 }}
|
||||
size="small">
|
||||
<Select.Option value="hover">hover</Select.Option>
|
||||
<Select.Option value="click">click</Select.Option>
|
||||
</Select>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('settings.messages.grid_columns')}</SettingRowTitleSmall>
|
||||
</SettingRow>
|
||||
<Row align="middle" gutter={10}>
|
||||
<Col span={24}>
|
||||
<Slider
|
||||
value={gridColumnsValue}
|
||||
onChange={(value) => setGridColumnsValue(value)}
|
||||
onChangeComplete={(value) => dispatch(setGridColumns(value))}
|
||||
min={2}
|
||||
max={9}
|
||||
step={1}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('settings.font_size.title')}</SettingRowTitleSmall>
|
||||
</SettingRow>
|
||||
|
||||
@ -44,6 +44,8 @@ export interface SettingsState {
|
||||
mathEngine: 'MathJax' | 'KaTeX'
|
||||
messageStyle: 'plain' | 'bubble'
|
||||
codeStyle: CodeStyleVarious
|
||||
gridColumns: number
|
||||
gridPopoverTrigger: 'hover' | 'click'
|
||||
// webdav 配置 host, user, pass, path
|
||||
webdavHost: string
|
||||
webdavUser: string
|
||||
@ -69,7 +71,7 @@ export interface SettingsState {
|
||||
notionApiKey: string | null
|
||||
}
|
||||
|
||||
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold'
|
||||
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid'
|
||||
|
||||
const initialState: SettingsState = {
|
||||
showAssistants: true,
|
||||
@ -99,6 +101,8 @@ const initialState: SettingsState = {
|
||||
mathEngine: 'KaTeX',
|
||||
messageStyle: 'plain',
|
||||
codeStyle: 'auto',
|
||||
gridColumns: 2,
|
||||
gridPopoverTrigger: 'hover',
|
||||
webdavHost: '',
|
||||
webdavUser: '',
|
||||
webdavPass: '',
|
||||
@ -224,6 +228,12 @@ const settingsSlice = createSlice({
|
||||
setMathEngine: (state, action: PayloadAction<'MathJax' | 'KaTeX'>) => {
|
||||
state.mathEngine = action.payload
|
||||
},
|
||||
setGridColumns: (state, action: PayloadAction<number>) => {
|
||||
state.gridColumns = action.payload
|
||||
},
|
||||
setGridPopoverTrigger: (state, action: PayloadAction<'hover' | 'click'>) => {
|
||||
state.gridPopoverTrigger = action.payload
|
||||
},
|
||||
setMessageStyle: (state, action: PayloadAction<'plain' | 'bubble'>) => {
|
||||
state.messageStyle = action.payload
|
||||
},
|
||||
@ -265,7 +275,7 @@ const settingsSlice = createSlice({
|
||||
setEnableQuickAssistant: (state, action: PayloadAction<boolean>) => {
|
||||
state.enableQuickAssistant = action.payload
|
||||
},
|
||||
setMultiModelMessageStyle: (state, action: PayloadAction<'horizontal' | 'vertical' | 'fold'>) => {
|
||||
setMultiModelMessageStyle: (state, action: PayloadAction<'horizontal' | 'vertical' | 'fold' | 'grid'>) => {
|
||||
state.multiModelMessageStyle = action.payload
|
||||
},
|
||||
setNotionDatabaseID: (state, action: PayloadAction<string>) => {
|
||||
@ -310,6 +320,8 @@ export const {
|
||||
setCodeShowLineNumbers,
|
||||
setCodeCollapsible,
|
||||
setMathEngine,
|
||||
setGridColumns,
|
||||
setGridPopoverTrigger,
|
||||
setMessageStyle,
|
||||
setCodeStyle,
|
||||
setTranslateModelPrompt,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user