diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bea18d50b5..7a007e4e91 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,7 +27,7 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v4
with:
- ref: main
+ fetch-depth: 0
- name: Get release tag
id: get-tag
@@ -149,4 +149,4 @@ jobs:
token: ${{ secrets.REPO_DISPATCH_TOKEN }}
repository: CherryHQ/cherry-studio-docs
event-type: update-download-version
- client-payload: '{"version": "${{ steps.get-tag.outputs.tag }}"}'
\ No newline at end of file
+ client-payload: '{"version": "${{ steps.get-tag.outputs.tag }}"}'
diff --git a/package.json b/package.json
index 6d0575bf65..adb3764281 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "CherryStudio",
- "version": "1.4.2",
+ "version": "1.4.2-ui-preview",
"private": true,
"description": "A powerful AI assistant for producer.",
"main": "./out/main/index.js",
@@ -193,8 +193,8 @@
"react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.2",
- "react-router": "6",
- "react-router-dom": "6",
+ "react-router": "^7.6.2",
+ "react-router-dom": "^7.6.2",
"react-spinners": "^0.14.1",
"react-window": "^1.8.11",
"redux": "^5.0.1",
diff --git a/src/main/config.ts b/src/main/config.ts
index 84dc1b846d..32665a55b8 100644
--- a/src/main/config.ts
+++ b/src/main/config.ts
@@ -11,13 +11,13 @@ if (isDev) {
export const DATA_PATH = getDataPath()
export const titleBarOverlayDark = {
- height: 40,
+ height: 42,
color: 'rgba(255,255,255,0)',
symbolColor: '#fff'
}
export const titleBarOverlayLight = {
- height: 40,
+ height: 42,
color: 'rgba(255,255,255,0)',
symbolColor: '#000'
}
diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts
index 3f37d7c406..469ba1bd5e 100644
--- a/src/main/services/WindowService.ts
+++ b/src/main/services/WindowService.ts
@@ -56,14 +56,14 @@ export class WindowService {
minHeight: 600,
show: false,
autoHideMenuBar: true,
- transparent: isMac,
+ transparent: false,
vibrancy: 'sidebar',
visualEffectState: 'active',
titleBarStyle: 'hidden',
titleBarOverlay: nativeTheme.shouldUseDarkColors ? titleBarOverlayDark : titleBarOverlayLight,
backgroundColor: isMac ? undefined : nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF',
darkTheme: nativeTheme.shouldUseDarkColors,
- trafficLightPosition: { x: 8, y: 12 },
+ trafficLightPosition: { x: 12, y: 12 },
...(isLinux ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
diff --git a/src/main/services/urlschema/mcp-install.ts b/src/main/services/urlschema/mcp-install.ts
index e5f0a76501..3131830a16 100644
--- a/src/main/services/urlschema/mcp-install.ts
+++ b/src/main/services/urlschema/mcp-install.ts
@@ -65,7 +65,7 @@ export function handleMcpProtocolUrl(url: URL) {
const mainWindow = windowService.getMainWindow()
if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.webContents.executeJavaScript("window.navigate('/settings/mcp')")
+ mainWindow.webContents.executeJavaScript("window.navigate('/mcp-servers')")
}
break
}
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index b46910cd65..a327cef59d 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -2,10 +2,9 @@ import '@renderer/databases'
import store, { persistor } from '@renderer/store'
import { Provider } from 'react-redux'
-import { HashRouter, Route, Routes } from 'react-router-dom'
+import { HashRouter } from 'react-router-dom'
import { PersistGate } from 'redux-persist/integration/react'
-import Sidebar from './components/app/Sidebar'
import TopViewContainer from './components/TopView'
import AntdProvider from './context/AntdProvider'
import { CodeStyleProvider } from './context/CodeStyleProvider'
@@ -13,14 +12,8 @@ import { NotificationProvider } from './context/NotificationProvider'
import StyleSheetManager from './context/StyleSheetManager'
import { ThemeProvider } from './context/ThemeProvider'
import NavigationHandler from './handler/NavigationHandler'
-import AgentsPage from './pages/agents/AgentsPage'
-import AppsPage from './pages/apps/AppsPage'
-import FilesPage from './pages/files/FilesPage'
-import HomePage from './pages/home/HomePage'
-import KnowledgePage from './pages/knowledge/KnowledgePage'
-import PaintingsRoutePage from './pages/paintings/PaintingsRoutePage'
-import SettingsPage from './pages/settings/SettingsPage'
-import TranslatePage from './pages/translate/TranslatePage'
+import MainSidebar from './pages/home/MainSidebar/MainSidebar'
+import Routes from './Routes'
function App(): React.ReactElement {
return (
@@ -34,17 +27,8 @@ function App(): React.ReactElement {
-
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
+
+
diff --git a/src/renderer/src/Routes.tsx b/src/renderer/src/Routes.tsx
new file mode 100644
index 0000000000..06e5c2e64b
--- /dev/null
+++ b/src/renderer/src/Routes.tsx
@@ -0,0 +1,38 @@
+import { Route, Routes, useLocation } from 'react-router-dom'
+
+import AgentsPage from './pages/agents/AgentsPage'
+import AppsPage from './pages/apps/AppsPage'
+import FilesPage from './pages/files/FilesPage'
+import HomePage from './pages/home/HomePage'
+import KnowledgePage from './pages/knowledge/KnowledgePage'
+import McpServersPage from './pages/mcp-servers'
+import PaintingsRoutePage from './pages/paintings/PaintingsRoutePage'
+import SettingsPage from './pages/settings/SettingsPage'
+import TranslatePage from './pages/translate/TranslatePage'
+
+const RouteContainer = () => {
+ const location = useLocation()
+ const isHomePage = location.pathname === '/'
+
+ return (
+
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+ )
+}
+
+export default RouteContainer
diff --git a/src/renderer/src/assets/styles/ant.scss b/src/renderer/src/assets/styles/ant.scss
index d4788439e5..f311510c0c 100644
--- a/src/renderer/src/assets/styles/ant.scss
+++ b/src/renderer/src/assets/styles/ant.scss
@@ -25,7 +25,6 @@
}
.minapp-drawer {
- max-width: calc(100vw - var(--sidebar-width));
.ant-drawer-content-wrapper {
box-shadow: none;
}
@@ -33,7 +32,7 @@
position: absolute;
-webkit-app-region: drag;
min-height: calc(var(--navbar-height) + 0.5px);
- width: calc(100vw - var(--sidebar-width));
+ width: 100%;
margin-top: -0.5px;
border-bottom: none;
}
diff --git a/src/renderer/src/assets/styles/color.scss b/src/renderer/src/assets/styles/color.scss
index 6100e1d0ee..5e87a1c0b5 100644
--- a/src/renderer/src/assets/styles/color.scss
+++ b/src/renderer/src/assets/styles/color.scss
@@ -29,7 +29,7 @@
--color-text-secondary: rgba(235, 235, 245, 0.7);
--color-icon: #ffffff99;
--color-icon-white: #ffffff;
- --color-border: #ffffff19;
+ --color-border: #383838;
--color-border-soft: #ffffff10;
--color-border-mute: #ffffff05;
--color-error: #f44336;
@@ -44,8 +44,8 @@
--color-reference-text: #ffffff;
--color-reference-background: #0b0e12;
- --color-list-item: #222;
- --color-list-item-hover: #1e1e1e;
+ --color-list-item: rgba(255, 255, 255, 0.1);
+ --color-list-item-hover: rgba(255, 255, 255, 0.05);
--modal-background: #1f1f1f;
@@ -56,7 +56,7 @@
--navbar-background-mac: rgba(20, 20, 20, 0.55);
--navbar-background: #1f1f1f;
- --navbar-height: 40px;
+ --navbar-height: 42px;
--sidebar-width: 50px;
--status-bar-height: 40px;
--input-bar-height: 100px;
@@ -66,12 +66,13 @@
--settings-width: 250px;
--scrollbar-width: 5px;
- --chat-background: #111111;
- --chat-background-user: #28b561;
- --chat-background-assistant: #2c2c2c;
+ --chat-background: transparent;
+ --chat-background-user: rgba(255, 255, 255, 0.08);
+ --chat-background-assistant: transparent;
--chat-text-user: var(--color-black);
- --list-item-border-radius: 20px;
+ --list-item-border-radius: 8px;
+ --border-width: 0.5px;
}
[theme-mode='light'] {
@@ -120,8 +121,8 @@
--color-reference-text: #000000;
--color-reference-background: #f1f7ff;
- --color-list-item: #eee;
- --color-list-item-hover: #f5f5f5;
+ --color-list-item: rgba(255, 255, 255, 0.9);
+ --color-list-item-hover: rgba(255, 255, 255, 0.5);
--modal-background: var(--color-white);
@@ -132,8 +133,10 @@
--navbar-background-mac: rgba(255, 255, 255, 0.55);
--navbar-background: rgba(244, 244, 244);
- --chat-background: #f3f3f3;
- --chat-background-user: #95ec69;
- --chat-background-assistant: #ffffff;
+ --chat-background: transparent;
+ --chat-background-user: rgba(0, 0, 0, 0.045);
+ --chat-background-assistant: transparent;
--chat-text-user: var(--color-text);
+
+ --border-width: 0.5px;
}
diff --git a/src/renderer/src/assets/styles/container.scss b/src/renderer/src/assets/styles/container.scss
index 8be4027981..c20e181060 100644
--- a/src/renderer/src/assets/styles/container.scss
+++ b/src/renderer/src/assets/styles/container.scss
@@ -1,6 +1,14 @@
#content-container {
background-color: var(--color-background);
border-top: 0.5px solid var(--color-border);
- border-top-left-radius: 10px;
- border-left: 0.5px solid var(--color-border);
+}
+
+.group-container {
+ .context-menu-container {
+ width: 100%;
+ }
+}
+
+.context-menu-container {
+ max-width: 100%;
}
diff --git a/src/renderer/src/assets/styles/index.scss b/src/renderer/src/assets/styles/index.scss
index b91b3c3a54..1c9b8a21f2 100644
--- a/src/renderer/src/assets/styles/index.scss
+++ b/src/renderer/src/assets/styles/index.scss
@@ -112,46 +112,21 @@ ul {
}
.bubble {
- background-color: var(--chat-background);
- #chat-main {
- background-color: var(--chat-background);
- }
- #messages {
- background-color: var(--chat-background);
- }
- #inputbar {
- margin: -5px 15px 15px 15px;
- background: var(--color-background);
- }
.system-prompt {
background-color: var(--chat-background-assistant);
}
.message-content-container {
margin: 5px 0;
border-radius: 8px;
- padding: 0.5rem 1rem;
}
-
- .block-wrapper {
- display: flow-root;
- }
-
- .message-content-container > *:last-child {
- margin-bottom: 0;
- }
-
.message-thought-container {
margin-top: 8px;
}
-
.message-user {
- color: var(--chat-text-user);
- .message-content-container-user .anticon {
- color: var(--chat-text-user) !important;
- }
-
- .markdown {
- color: var(--chat-text-user);
+ .message-content-container {
+ margin: 5px 0;
+ border-radius: 8px 0 8px 8px;
+ padding: 10px 15px 0 15px;
}
}
.group-grid-container.horizontal,
@@ -172,12 +147,6 @@ ul {
code {
color: var(--color-text);
}
- .markdown {
- display: flow-root;
- *:last-child {
- margin-bottom: 0;
- }
- }
}
.lucide {
diff --git a/src/renderer/src/assets/styles/markdown.scss b/src/renderer/src/assets/styles/markdown.scss
index 569fa9f1dd..d2b77d4016 100644
--- a/src/renderer/src/assets/styles/markdown.scss
+++ b/src/renderer/src/assets/styles/markdown.scss
@@ -119,7 +119,7 @@
}
pre {
- border-radius: 5px;
+ border-radius: 8px;
overflow-x: auto;
font-family: 'Fira Code', 'Courier New', Courier, monospace;
background-color: var(--color-background-mute);
@@ -306,7 +306,7 @@ mjx-container {
/* CodeMirror 相关样式 */
.cm-editor {
- border-radius: 5px;
+ border-radius: inherit;
&.cm-focused {
outline: none;
diff --git a/src/renderer/src/components/CodeBlockView/CodePreview.tsx b/src/renderer/src/components/CodeBlockView/CodePreview.tsx
index d3c56f295b..04990d4d87 100644
--- a/src/renderer/src/components/CodeBlockView/CodePreview.tsx
+++ b/src/renderer/src/components/CodeBlockView/CodePreview.tsx
@@ -238,12 +238,12 @@ const ContentContainer = styled.div<{
}>`
position: relative;
overflow: auto;
- border: 0.5px solid transparent;
- border-radius: 5px;
+ border-radius: inherit;
margin-top: 0;
.shiki {
padding: 1em;
+ border-radius: inherit;
code {
display: flex;
diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifacts.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifacts.tsx
index 0dbb0aabb2..ccf760d27b 100644
--- a/src/renderer/src/components/CodeBlockView/HtmlArtifacts.tsx
+++ b/src/renderer/src/components/CodeBlockView/HtmlArtifacts.tsx
@@ -47,7 +47,7 @@ const Artifacts: FC = ({ html }) => {
}
return (
-
+
} onClick={handleOpenInApp}>
{t('chat.artifacts.button.preview')}
diff --git a/src/renderer/src/components/CodeBlockView/index.tsx b/src/renderer/src/components/CodeBlockView/index.tsx
index 944e6f66e3..64a3bb6c02 100644
--- a/src/renderer/src/components/CodeBlockView/index.tsx
+++ b/src/renderer/src/components/CodeBlockView/index.tsx
@@ -290,6 +290,10 @@ const SplitViewWrapper = styled.div`
flex: 1 1 auto;
width: 100%;
}
+
+ &:not(:has(+ .html-artifacts)) {
+ border-radius: 0 0 8px 8px;
+ }
`
export default memo(CodeBlockView)
diff --git a/src/renderer/src/components/CodeEditor/index.tsx b/src/renderer/src/components/CodeEditor/index.tsx
index d92fd91e8e..977eea6ac0 100644
--- a/src/renderer/src/components/CodeEditor/index.tsx
+++ b/src/renderer/src/components/CodeEditor/index.tsx
@@ -229,8 +229,8 @@ const CodeEditor = ({
style={{
...style,
fontSize: `${fontSize - 1}px`,
- border: '0.5px solid transparent',
- marginTop: 0
+ marginTop: 0,
+ borderRadius: 'inherit'
}}
/>
)
diff --git a/src/renderer/src/components/ContextMenu/index.tsx b/src/renderer/src/components/ContextMenu/index.tsx
index 195fcb2a38..bb3136067c 100644
--- a/src/renderer/src/components/ContextMenu/index.tsx
+++ b/src/renderer/src/components/ContextMenu/index.tsx
@@ -6,10 +6,9 @@ import styled from 'styled-components'
interface ContextMenuProps {
children: React.ReactNode
onContextMenu?: (e: React.MouseEvent) => void
- style?: React.CSSProperties
}
-const ContextMenu: React.FC = ({ children, onContextMenu, style }) => {
+const ContextMenu: React.FC = ({ children, onContextMenu }) => {
const { t } = useTranslation()
const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null)
const [selectedText, setSelectedText] = useState('')
@@ -67,7 +66,7 @@ const ContextMenu: React.FC = ({ children, onContextMenu, styl
]
return (
-
+
{contextMenuPosition && (
> = ({
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
+ marginBottom: 8,
...listStyle,
- ...provided.draggableProps.style,
- marginBottom: 8
+ ...provided.draggableProps.style
}}>
{children(item, index)}
diff --git a/src/renderer/src/components/Icons/NarrowModeIcon.tsx b/src/renderer/src/components/Icons/NarrowModeIcon.tsx
new file mode 100644
index 0000000000..ac3bc01513
--- /dev/null
+++ b/src/renderer/src/components/Icons/NarrowModeIcon.tsx
@@ -0,0 +1,35 @@
+import { FC } from 'react'
+import styled from 'styled-components'
+
+interface Props {
+ isNarrowMode: boolean
+}
+
+const NarrowModeIcon: FC = ({ isNarrowMode }) => {
+ return (
+
+
+
+
+ )
+}
+
+const Container = styled.div<{ $isNarrowMode: boolean }>`
+ width: 16px;
+ height: 16px;
+ border: 1.5px solid var(--color-text-2);
+ border-radius: 3px;
+ display: flex;
+ align-items: center;
+ justify-content: ${({ $isNarrowMode }) => ($isNarrowMode ? 'space-evenly' : 'space-between')};
+ padding: 2px;
+`
+
+const Line = styled.div`
+ width: 1.5px;
+ height: 10px;
+ background-color: var(--color-text-2);
+ border-radius: 5px;
+`
+
+export default NarrowModeIcon
diff --git a/src/renderer/src/components/Icons/SVGIcon.tsx b/src/renderer/src/components/Icons/SVGIcon.tsx
index d58eab7ee5..a146bbcd0b 100644
--- a/src/renderer/src/components/Icons/SVGIcon.tsx
+++ b/src/renderer/src/components/Icons/SVGIcon.tsx
@@ -66,3 +66,18 @@ export function MdiLightbulbOn90(props: SVGProps) {
)
}
+
+export function ExpandWidth(props: SVGProps) {
+ return (
+
+ )
+}
diff --git a/src/renderer/src/components/MinApp/MinappPopupContainer.tsx b/src/renderer/src/components/MinApp/MinappPopupContainer.tsx
index bd360c8a30..252c25fe14 100644
--- a/src/renderer/src/components/MinApp/MinappPopupContainer.tsx
+++ b/src/renderer/src/components/MinApp/MinappPopupContainer.tsx
@@ -395,10 +395,7 @@ const MinappPopupContainer: React.FC = () => {
height={'100%'}
maskClosable={false}
closeIcon={null}
- style={{
- marginLeft: 'var(--sidebar-width)',
- backgroundColor: window.root.style.background
- }}>
+ style={{ backgroundColor: window.root.style.background }}>
{!isReady && (
= ({
- children,
- activeAssistant,
- setActiveAssistant,
- activeTopic,
- setActiveTopic,
- position = 'left'
-}) => {
+const FloatingSidebar: FC = ({ children, activeAssistant, setActiveAssistant, activeTopic, setActiveTopic }) => {
const [open, setOpen] = useState(false)
useHotkeys('esc', () => {
@@ -45,12 +38,11 @@ const FloatingSidebar: FC = ({
const content = (
void
+}
+
+const PopupContainer: React.FC = ({ title, resolve }) => {
+ const [open, setOpen] = useState(true)
+
+ const { messageStyle, fontSize } = useSettings()
+ const { theme } = useTheme()
+ const { themeNames } = useCodeStyle()
+
+ const [fontSizeValue, setFontSizeValue] = useState(fontSize)
+ const { t } = useTranslation()
+
+ const dispatch = useAppDispatch()
+
+ const {
+ showMessageDivider,
+ messageFont,
+ codeShowLineNumbers,
+ codeCollapsible,
+ codeWrappable,
+ codeEditor,
+ codePreview,
+ codeExecution,
+ mathEngine,
+ multiModelMessageStyle,
+ thoughtAutoCollapse,
+ messageNavigation
+ } = useSettings()
+
+ const codeStyle = useMemo(() => {
+ return codeEditor.enabled
+ ? theme === ThemeMode.light
+ ? codeEditor.themeLight
+ : codeEditor.themeDark
+ : theme === ThemeMode.light
+ ? codePreview.themeLight
+ : codePreview.themeDark
+ }, [
+ codeEditor.enabled,
+ codeEditor.themeLight,
+ codeEditor.themeDark,
+ theme,
+ codePreview.themeLight,
+ codePreview.themeDark
+ ])
+
+ const onCodeStyleChange = useCallback(
+ (value: CodeStyleVarious) => {
+ const field = theme === ThemeMode.light ? 'themeLight' : 'themeDark'
+ const action = codeEditor.enabled ? setCodeEditor : setCodePreview
+ dispatch(action({ [field]: value }))
+ },
+ [dispatch, theme, codeEditor.enabled]
+ )
+
+ const onOk = () => {
+ resolve(true)
+ setOpen(false)
+ }
+
+ const onCancel = () => {
+ resolve(false)
+ setOpen(false)
+ }
+
+ const onClose = () => {
+ TopView.hide(TopViewKey)
+ }
+
+ MessageSettingsPopup.hide = onCancel
+
+ return (
+
+
+
+ {t('settings.messages.divider')}
+ dispatch(setShowMessageDivider(checked))}
+ />
+
+
+
+ {t('settings.messages.use_serif_font')}
+ dispatch(setMessageFont(checked ? 'serif' : 'system'))}
+ />
+
+
+
+
+ {t('chat.settings.thought_auto_collapse')}
+
+
+
+
+ dispatch(setThoughtAutoCollapse(checked))}
+ />
+
+
+
+ {t('message.message.style')}
+ dispatch(setMessageStyle(value as 'plain' | 'bubble'))}
+ style={{ width: 135 }}
+ size="small">
+ {t('message.message.style.plain')}
+ {t('message.message.style.bubble')}
+
+
+
+
+ {t('message.message.multi_model_style')}
+
+ dispatch(setMultiModelMessageStyle(value as 'fold' | 'vertical' | 'horizontal' | 'grid'))
+ }
+ style={{ width: 135 }}>
+ {t('message.message.multi_model_style.fold')}
+ {t('message.message.multi_model_style.vertical')}
+ {t('message.message.multi_model_style.horizontal')}
+ {t('message.message.multi_model_style.grid')}
+
+
+
+
+ {t('settings.messages.navigation')}
+ dispatch(setMessageNavigation(value as 'none' | 'buttons' | 'anchor'))}
+ style={{ width: 135 }}>
+ {t('settings.messages.navigation.none')}
+ {t('settings.messages.navigation.buttons')}
+ {t('settings.messages.navigation.anchor')}
+
+
+
+
+ {t('settings.messages.math_engine')}
+ dispatch(setMathEngine(value as MathEngine))}
+ style={{ width: 135 }}
+ size="small">
+ KaTeX
+ MathJax
+ {t('settings.messages.math_engine.none')}
+
+
+
+
+ {t('settings.font_size.title')}
+
+
+
+ setFontSizeValue(value)}
+ onChangeComplete={(value) => dispatch(setFontSize(value))}
+ min={12}
+ max={22}
+ step={1}
+ marks={{
+ 12: A,
+ 14: {t('common.default')},
+ 22: A
+ }}
+ />
+
+
+
+
+
+ {t('message.message.code_style')}
+ onCodeStyleChange(value as CodeStyleVarious)}
+ style={{ width: 135 }}
+ size="small">
+ {themeNames.map((theme) => (
+
+ {theme}
+
+ ))}
+
+
+
+
+
+ {t('chat.settings.code_execution.title')}
+
+
+
+
+ dispatch(setCodeExecution({ enabled: checked }))}
+ />
+
+ {codeExecution.enabled && (
+ <>
+
+
+
+ {t('chat.settings.code_execution.timeout_minutes')}
+
+
+
+
+ dispatch(setCodeExecution({ timeoutMinutes: value ?? 1 }))}
+ style={{ width: 80 }}
+ />
+
+ >
+ )}
+
+
+ {t('chat.settings.code_editor.title')}
+ dispatch(setCodeEditor({ enabled: checked }))}
+ />
+
+ {codeEditor.enabled && (
+ <>
+
+
+ {t('chat.settings.code_editor.highlight_active_line')}
+ dispatch(setCodeEditor({ highlightActiveLine: checked }))}
+ />
+
+
+
+ {t('chat.settings.code_editor.fold_gutter')}
+ dispatch(setCodeEditor({ foldGutter: checked }))}
+ />
+
+
+
+ {t('chat.settings.code_editor.autocompletion')}
+ dispatch(setCodeEditor({ autocompletion: checked }))}
+ />
+
+
+
+ {t('chat.settings.code_editor.keymap')}
+ dispatch(setCodeEditor({ keymap: checked }))}
+ />
+
+ >
+ )}
+
+
+ {t('chat.settings.show_line_numbers')}
+ dispatch(setCodeShowLineNumbers(checked))}
+ />
+
+
+
+ {t('chat.settings.code_collapsible')}
+ dispatch(setCodeCollapsible(checked))}
+ />
+
+
+
+ {t('chat.settings.code_wrappable')}
+ dispatch(setCodeWrappable(checked))} />
+
+
+
+ )
+}
+
+const SettingRowTitleSmall = styled(SettingRowTitle)`
+ font-size: 13px;
+`
+
+const SettingGroup = styled.div<{ theme?: ThemeMode }>`
+ padding: 0;
+ width: 100%;
+ margin-top: 0;
+ border-radius: 8px;
+ margin-bottom: 10px;
+ margin-top: 10px;
+`
+
+const StyledSelect = styled(Select)`
+ .ant-select-selector {
+ border-radius: 15px !important;
+ padding: 4px 10px !important;
+ height: 26px !important;
+ }
+`
+
+const TopViewKey = 'MessageSettingsPopup'
+
+export default class MessageSettingsPopup {
+ static topviewId = 0
+ static hide() {
+ TopView.hide(TopViewKey)
+ }
+ static show(props: ShowParams) {
+ return new Promise((resolve) => {
+ TopView.show(, TopViewKey)
+ })
+ }
+}
diff --git a/src/renderer/src/components/Popups/TemplatePopup.tsx b/src/renderer/src/components/Popups/TemplatePopup.tsx
index 67dc50febf..f9b0494899 100644
--- a/src/renderer/src/components/Popups/TemplatePopup.tsx
+++ b/src/renderer/src/components/Popups/TemplatePopup.tsx
@@ -15,15 +15,17 @@ const PopupContainer: React.FC = ({ title, resolve }) => {
const [open, setOpen] = useState(true)
const onOk = () => {
+ resolve(true)
setOpen(false)
}
const onCancel = () => {
+ resolve(false)
setOpen(false)
}
const onClose = () => {
- resolve({})
+ TopView.hide(TopViewKey)
}
TemplatePopup.hide = onCancel
@@ -51,16 +53,7 @@ export default class TemplatePopup {
}
static show(props: ShowParams) {
return new Promise((resolve) => {
- TopView.show(
- {
- resolve(v)
- TopView.hide(TopViewKey)
- }}
- />,
- TopViewKey
- )
+ TopView.show(, TopViewKey)
})
}
}
diff --git a/src/renderer/src/components/Selector.tsx b/src/renderer/src/components/Selector.tsx
new file mode 100644
index 0000000000..f405b1202d
--- /dev/null
+++ b/src/renderer/src/components/Selector.tsx
@@ -0,0 +1,76 @@
+import { ConfigProvider, Dropdown } from 'antd'
+import { Check, ChevronsUpDown } from 'lucide-react'
+import { FC, useMemo } from 'react'
+import styled from 'styled-components'
+
+interface SelectorProps {
+ options: { label: string; value: string }[]
+ value: string | number | undefined
+ placement?: 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight' | 'top' | 'bottom'
+ /** 字体大小 */
+ size?: number
+ onChange: (value: string) => void
+}
+
+const Selector: FC = ({ options, value, onChange, placement = 'bottomRight', size = 13 }) => {
+ const label = useMemo(() => options?.find((option) => option.value === value)?.label, [options, value])
+
+ const items = useMemo(() => {
+ return options.map((option) => ({
+ key: option.value,
+ label: option.label,
+ extra: {option.value === value && }
+ }))
+ }, [options, value])
+
+ function onClick(e: { key: string }) {
+ onChange(e.key)
+ }
+
+ return (
+
+
+
+
+
+ )
+}
+
+const Label = styled.div<{ $size: number }>`
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ border-radius: 99px;
+ padding: 1px 2px 1px 10px;
+ font-size: ${({ $size }) => $size}px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ &:hover {
+ background-color: var(--color-background-mute);
+ }
+`
+
+const LabelIcon = styled(ChevronsUpDown)`
+ border-radius: 4px;
+ padding: 2px 0;
+ background-color: var(--color-background-mute);
+`
+
+const CheckIcon = styled.div`
+ width: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: end;
+`
+
+export default Selector
diff --git a/src/renderer/src/components/TranslateButton.tsx b/src/renderer/src/components/TranslateButton.tsx
index 78dd1b7d34..b96341d777 100644
--- a/src/renderer/src/components/TranslateButton.tsx
+++ b/src/renderer/src/components/TranslateButton.tsx
@@ -78,7 +78,7 @@ const TranslateButton: FC = ({ text, onTranslated, disabled, style, isLoa
title={t('chat.input.translate', { target_language: t(`languages.${targetLanguage.toString()}`) })}
arrow>
- {isTranslating ? : }
+ {isTranslating ? : }
)
diff --git a/src/renderer/src/components/app/Navbar.tsx b/src/renderer/src/components/app/Navbar.tsx
index e6176c2a00..384622c96f 100644
--- a/src/renderer/src/components/app/Navbar.tsx
+++ b/src/renderer/src/components/app/Navbar.tsx
@@ -1,24 +1,16 @@
import { isLinux, isMac, isWindows } from '@renderer/config/constant'
import { useFullscreen } from '@renderer/hooks/useFullscreen'
-import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
+import { Button } from 'antd'
+import { CircleArrowLeft, X } from 'lucide-react'
import type { FC, PropsWithChildren } from 'react'
import type { HTMLAttributes } from 'react'
+import { useNavigate } from 'react-router-dom'
import styled from 'styled-components'
type Props = PropsWithChildren & HTMLAttributes
export const Navbar: FC = ({ children, ...props }) => {
- const backgroundColor = useNavBackgroundColor()
-
- return (
-
- {children}
-
- )
-}
-
-export const NavbarLeft: FC = ({ children, ...props }) => {
- return {children}
+ return {children}
}
export const NavbarCenter: FC = ({ children, ...props }) => {
@@ -36,41 +28,52 @@ export const NavbarRight: FC = ({ children, ...props }) => {
export const NavbarMain: FC = ({ children, ...props }) => {
const isFullscreen = useFullscreen()
+
return (
+
{children}
+
)
}
+const MacCloseIcon = () => {
+ const navigate = useNavigate()
+
+ if (!isMac) {
+ return null
+ }
+
+ return } onClick={() => navigate('/')} className="nodrag" />
+}
+
+const CloseIcon = () => {
+ const navigate = useNavigate()
+
+ if (isMac) {
+ return null
+ }
+
+ return (
+