feat: code collapsible settings
This commit is contained in:
parent
84fa5b065b
commit
5bffb86d4f
@ -113,6 +113,7 @@
|
||||
"settings.set_as_default": "Apply to default assistant",
|
||||
"settings.max": "Max",
|
||||
"settings.show_line_numbers": "Show Line Numbers in Code",
|
||||
"settings.code_collapsible": "Code block collapsible",
|
||||
"suggestions.title": "Suggested Questions",
|
||||
"add.assistant.title": "Add Assistant",
|
||||
"message.new.context": "New Context",
|
||||
|
||||
@ -113,6 +113,7 @@
|
||||
"settings.set_as_default": "Применить к ассистенту по умолчанию",
|
||||
"settings.max": "Максимум",
|
||||
"settings.show_line_numbers": "Показать номера строк в коде",
|
||||
"settings.code_collapsible": "Блок кода свернут",
|
||||
"suggestions.title": "Предложенные вопросы",
|
||||
"add.assistant.title": "Добавить ассистента",
|
||||
"message.new.context": "Новый контекст",
|
||||
|
||||
@ -113,6 +113,7 @@
|
||||
"settings.set_as_default": "应用到默认助手",
|
||||
"settings.max": "不限",
|
||||
"settings.show_line_numbers": "代码显示行号",
|
||||
"settings.code_collapsible": "代码块可折叠",
|
||||
"suggestions.title": "建议的问题",
|
||||
"add.assistant.title": "添加助手",
|
||||
"message.new.context": "清除上下文",
|
||||
|
||||
@ -113,6 +113,7 @@
|
||||
"settings.set_as_default": "設為預設助手",
|
||||
"settings.max": "最大",
|
||||
"settings.show_line_numbers": "代码顯示行號",
|
||||
"settings.code_collapsible": "代码块可折叠",
|
||||
"suggestions.title": "建議的問題",
|
||||
"add.assistant.title": "添加助手",
|
||||
"message.new.context": "新上下文",
|
||||
|
||||
@ -2,7 +2,7 @@ import { CheckOutlined, DownOutlined, RightOutlined } from '@ant-design/icons'
|
||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||
import { useSyntaxHighlighter } from '@renderer/context/SyntaxHighlighterProvider'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import React, { memo, useEffect, useState } from 'react'
|
||||
import React, { memo, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -16,10 +16,10 @@ interface CodeBlockProps {
|
||||
}
|
||||
|
||||
const CollapseIcon: React.FC<{ expanded: boolean; onClick: () => void }> = ({ expanded, onClick }) => {
|
||||
return expanded ? (
|
||||
<DownOutlined style={{ cursor: 'pointer' }} onClick={onClick} />
|
||||
) : (
|
||||
<RightOutlined style={{ cursor: 'pointer' }} onClick={onClick} />
|
||||
return (
|
||||
<CollapseIconWrapper onClick={onClick}>
|
||||
{expanded ? <DownOutlined style={{ fontSize: 12 }} /> : <RightOutlined style={{ fontSize: 12 }} />}
|
||||
</CollapseIconWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@ -31,28 +31,22 @@ const ExpandButton: React.FC<{
|
||||
if (!showButton) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
padding: '8px',
|
||||
color: 'var(--color-text-3)',
|
||||
borderTop: '0.5px solid var(--color-code-background)'
|
||||
}}
|
||||
onClick={onClick}>
|
||||
{isExpanded ? '收起' : '展开'}
|
||||
</div>
|
||||
<ExpandButtonWrapper onClick={onClick}>
|
||||
<div className="button-text">{isExpanded ? '收起' : '展开'}</div>
|
||||
</ExpandButtonWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
const showFooterCopyButton = children && children.length > 500
|
||||
const { codeShowLineNumbers, fontSize } = useSettings()
|
||||
const { codeShowLineNumbers, fontSize, codeCollapsible } = useSettings()
|
||||
const language = match?.[1] ?? 'text'
|
||||
const [html, setHtml] = useState<string>('')
|
||||
const { codeToHtml } = useSyntaxHighlighter()
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [isExpanded, setIsExpanded] = useState(!codeCollapsible)
|
||||
const [shouldShowExpandButton, setShouldShowExpandButton] = useState(false)
|
||||
const codeContentRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const loadHighlightedCode = async () => {
|
||||
@ -62,6 +56,24 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
|
||||
loadHighlightedCode()
|
||||
}, [children, language, codeToHtml])
|
||||
|
||||
useEffect(() => {
|
||||
if (codeContentRef.current) {
|
||||
setShouldShowExpandButton(codeContentRef.current.scrollHeight > 350)
|
||||
}
|
||||
}, [html])
|
||||
|
||||
useEffect(() => {
|
||||
if (!codeCollapsible) {
|
||||
setIsExpanded(true)
|
||||
setShouldShowExpandButton(false)
|
||||
} else {
|
||||
setIsExpanded(!codeCollapsible)
|
||||
if (codeContentRef.current) {
|
||||
setShouldShowExpandButton(codeContentRef.current.scrollHeight > 350)
|
||||
}
|
||||
}
|
||||
}, [codeCollapsible])
|
||||
|
||||
if (language === 'mermaid') {
|
||||
return <Mermaid chart={children} />
|
||||
}
|
||||
@ -70,12 +82,13 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
|
||||
<div className="code-block">
|
||||
<CodeHeader>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<CollapseIcon expanded={isExpanded} onClick={() => setIsExpanded(!isExpanded)} />
|
||||
{codeCollapsible && <CollapseIcon expanded={isExpanded} onClick={() => setIsExpanded(!isExpanded)} />}
|
||||
<CodeLanguage>{'<' + match[1].toUpperCase() + '>'}</CodeLanguage>
|
||||
</div>
|
||||
<CopyButton text={children} />
|
||||
</CodeHeader>
|
||||
<CodeContent
|
||||
ref={codeContentRef}
|
||||
isShowLineNumbers={codeShowLineNumbers}
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
style={{
|
||||
@ -83,17 +96,19 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
marginTop: 0,
|
||||
fontSize,
|
||||
maxHeight: isExpanded ? 'none' : '300px',
|
||||
overflow: 'hidden',
|
||||
fontSize: fontSize - 1,
|
||||
maxHeight: codeCollapsible && !isExpanded ? '350px' : 'none',
|
||||
overflow: codeCollapsible && !isExpanded ? 'auto' : 'visible',
|
||||
position: 'relative'
|
||||
}}
|
||||
/>
|
||||
<ExpandButton
|
||||
isExpanded={isExpanded}
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
showButton={!isExpanded || showFooterCopyButton}
|
||||
/>
|
||||
{codeCollapsible && (
|
||||
<ExpandButton
|
||||
isExpanded={isExpanded}
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
showButton={shouldShowExpandButton}
|
||||
/>
|
||||
)}
|
||||
{showFooterCopyButton && (
|
||||
<CodeFooter>
|
||||
<CopyButton text={children} style={{ marginTop: -40, marginRight: 10 }} />
|
||||
@ -189,4 +204,45 @@ const CodeFooter = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const ExpandButtonWrapper = styled.div`
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
margin-top: -30px;
|
||||
|
||||
.button-text {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
color: var(--color-text-3);
|
||||
z-index: 1;
|
||||
transition: color 0.2s;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover .button-text {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
`
|
||||
|
||||
const CollapseIconWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-3);
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-soft);
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
`
|
||||
|
||||
export default memo(CodeBlock)
|
||||
|
||||
@ -6,6 +6,7 @@ import { SettingDivider, SettingRow, SettingRowTitle, SettingSubtitle } from '@r
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import {
|
||||
setClickAssistantToShowTopic,
|
||||
setCodeCollapsible,
|
||||
setCodeShowLineNumbers,
|
||||
setCodeStyle,
|
||||
setFontSize,
|
||||
@ -39,6 +40,7 @@ const SettingsTab: FC = () => {
|
||||
pasteLongTextAsFile,
|
||||
renderInputMessageAsMarkdown,
|
||||
codeShowLineNumbers,
|
||||
codeCollapsible,
|
||||
mathEngine,
|
||||
topicPosition,
|
||||
showTopicTime,
|
||||
@ -81,6 +83,11 @@ const SettingsTab: FC = () => {
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_collapsible')}</SettingRowTitleSmall>
|
||||
<Switch size="small" checked={codeCollapsible} onChange={(checked) => dispatch(setCodeCollapsible(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('message.message.style')}</SettingRowTitleSmall>
|
||||
<Select
|
||||
|
||||
@ -25,6 +25,7 @@ export interface SettingsState {
|
||||
manualUpdateCheck: boolean
|
||||
renderInputMessageAsMarkdown: boolean
|
||||
codeShowLineNumbers: boolean
|
||||
codeCollapsible: boolean
|
||||
mathEngine: 'MathJax' | 'KaTeX'
|
||||
messageStyle: 'plain' | 'bubble'
|
||||
codeStyle: CodeStyleVarious
|
||||
@ -57,6 +58,7 @@ const initialState: SettingsState = {
|
||||
manualUpdateCheck: false,
|
||||
renderInputMessageAsMarkdown: true,
|
||||
codeShowLineNumbers: false,
|
||||
codeCollapsible: false,
|
||||
mathEngine: 'MathJax',
|
||||
messageStyle: 'plain',
|
||||
codeStyle: 'auto',
|
||||
@ -128,6 +130,9 @@ const settingsSlice = createSlice({
|
||||
setPasteLongTextAsFile: (state, action: PayloadAction<boolean>) => {
|
||||
state.pasteLongTextAsFile = action.payload
|
||||
},
|
||||
setRenderInputMessageAsMarkdown: (state, action: PayloadAction<boolean>) => {
|
||||
state.renderInputMessageAsMarkdown = action.payload
|
||||
},
|
||||
setClickAssistantToShowTopic: (state, action: PayloadAction<boolean>) => {
|
||||
state.clickAssistantToShowTopic = action.payload
|
||||
},
|
||||
@ -146,12 +151,12 @@ const settingsSlice = createSlice({
|
||||
setWebdavPath: (state, action: PayloadAction<string>) => {
|
||||
state.webdavPath = action.payload
|
||||
},
|
||||
setRenderInputMessageAsMarkdown: (state, action: PayloadAction<boolean>) => {
|
||||
state.renderInputMessageAsMarkdown = action.payload
|
||||
},
|
||||
setCodeShowLineNumbers: (state, action: PayloadAction<boolean>) => {
|
||||
state.codeShowLineNumbers = action.payload
|
||||
},
|
||||
setCodeCollapsible: (state, action: PayloadAction<boolean>) => {
|
||||
state.codeCollapsible = action.payload
|
||||
},
|
||||
setMathEngine: (state, action: PayloadAction<'MathJax' | 'KaTeX'>) => {
|
||||
state.mathEngine = action.payload
|
||||
},
|
||||
@ -192,6 +197,7 @@ export const {
|
||||
setWebdavPass,
|
||||
setWebdavPath,
|
||||
setCodeShowLineNumbers,
|
||||
setCodeCollapsible,
|
||||
setMathEngine,
|
||||
setMessageStyle,
|
||||
setCodeStyle
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user