feat: transparent window settings
This commit is contained in:
parent
9e2c7a08df
commit
14acd45927
@ -48,8 +48,8 @@
|
|||||||
--status-bar-height: 40px;
|
--status-bar-height: 40px;
|
||||||
--input-bar-height: 85px;
|
--input-bar-height: 85px;
|
||||||
|
|
||||||
--assistants-width: 300px;
|
--assistants-width: 280px;
|
||||||
--topic-list-width: 300px;
|
--topic-list-width: 280px;
|
||||||
--settings-width: var(--assistants-width);
|
--settings-width: var(--assistants-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,8 +70,8 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
|||||||
open={open}
|
open={open}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
afterClose={onClose}
|
afterClose={onClose}
|
||||||
transitionName=""
|
transitionName="ant-move-down"
|
||||||
maskTransitionName=""
|
maskTransitionName="ant-fade"
|
||||||
footer={null}>
|
footer={null}>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('common.search')}
|
placeholder={t('common.search')}
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useRuntime } from '@renderer/hooks/useStore'
|
import { useRuntime } from '@renderer/hooks/useStore'
|
||||||
import { FC, PropsWithChildren } from 'react'
|
import { FC, PropsWithChildren } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
type Props = PropsWithChildren & JSX.IntrinsicElements['div']
|
type Props = PropsWithChildren & JSX.IntrinsicElements['div']
|
||||||
|
|
||||||
const navbarBackgroundColor = isMac ? 'var(--navbar-background-mac)' : 'var(--navbar-background)'
|
|
||||||
|
|
||||||
export const Navbar: FC<Props> = ({ children, ...props }) => {
|
export const Navbar: FC<Props> = ({ children, ...props }) => {
|
||||||
const { minappShow } = useRuntime()
|
const { minappShow } = useRuntime()
|
||||||
const backgroundColor = minappShow ? 'var(--navbar-background)' : navbarBackgroundColor
|
const { windowStyle } = useSettings()
|
||||||
|
|
||||||
|
const macTransparentWindow = isMac && windowStyle === 'transparent'
|
||||||
|
const navbarBgColor = macTransparentWindow ? 'var(--navbar-background-mac)' : 'var(--navbar-background)'
|
||||||
|
const backgroundColor = minappShow ? 'var(--navbar-background)' : navbarBgColor
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavbarContainer {...props} style={{ backgroundColor }}>
|
<NavbarContainer {...props} style={{ backgroundColor }}>
|
||||||
@ -39,7 +42,6 @@ const NavbarContainer = styled.div`
|
|||||||
margin-left: ${isMac ? 'calc(var(--sidebar-width) * -1)' : 0};
|
margin-left: ${isMac ? 'calc(var(--sidebar-width) * -1)' : 0};
|
||||||
padding-left: ${isMac ? 'var(--sidebar-width)' : 0};
|
padding-left: ${isMac ? 'var(--sidebar-width)' : 0};
|
||||||
border-bottom: 0.5px solid var(--color-border);
|
border-bottom: 0.5px solid var(--color-border);
|
||||||
background-color: ${navbarBackgroundColor};
|
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
`
|
`
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { TranslationOutlined } from '@ant-design/icons'
|
|||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { isLocalAi, UserAvatar } from '@renderer/config/env'
|
import { isLocalAi, UserAvatar } from '@renderer/config/env'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useRuntime, useShowAssistants } from '@renderer/hooks/useStore'
|
import { useRuntime, useShowAssistants } from '@renderer/hooks/useStore'
|
||||||
import { Avatar } from 'antd'
|
import { Avatar } from 'antd'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
@ -11,8 +12,6 @@ import styled from 'styled-components'
|
|||||||
|
|
||||||
import UserPopup from '../Popups/UserPopup'
|
import UserPopup from '../Popups/UserPopup'
|
||||||
|
|
||||||
const sidebarBackgroundColor = isMac ? 'var(--navbar-background-mac)' : 'var(--navbar-background)'
|
|
||||||
|
|
||||||
const Sidebar: FC = () => {
|
const Sidebar: FC = () => {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
const avatar = useAvatar()
|
const avatar = useAvatar()
|
||||||
@ -21,11 +20,15 @@ const Sidebar: FC = () => {
|
|||||||
const { generating } = useRuntime()
|
const { generating } = useRuntime()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { windowStyle } = useSettings()
|
||||||
|
|
||||||
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
||||||
|
|
||||||
const onEditUser = () => UserPopup.show()
|
const onEditUser = () => UserPopup.show()
|
||||||
|
|
||||||
|
const macTransparentWindow = isMac && windowStyle === 'transparent'
|
||||||
|
const sidebarBgColor = macTransparentWindow ? 'var(--navbar-background-mac)' : 'var(--navbar-background)'
|
||||||
|
|
||||||
const to = (path: string) => {
|
const to = (path: string) => {
|
||||||
if (generating) {
|
if (generating) {
|
||||||
window.message.warning({ content: t('message.switch.disabled'), key: 'switch-assistant' })
|
window.message.warning({ content: t('message.switch.disabled'), key: 'switch-assistant' })
|
||||||
@ -39,7 +42,7 @@ const Sidebar: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container style={{ backgroundColor: minappShow ? 'var(--navbar-background)' : sidebarBackgroundColor }}>
|
<Container style={{ backgroundColor: minappShow ? 'var(--navbar-background)' : sidebarBgColor }}>
|
||||||
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
|
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
|
||||||
<MainMenus>
|
<MainMenus>
|
||||||
<Menus>
|
<Menus>
|
||||||
@ -87,7 +90,6 @@ const Container = styled.div`
|
|||||||
-webkit-app-region: drag !important;
|
-webkit-app-region: drag !important;
|
||||||
border-right: 0.5px solid var(--color-border);
|
border-right: 0.5px solid var(--color-border);
|
||||||
margin-top: ${isMac ? 'var(--navbar-height)' : 0};
|
margin-top: ${isMac ? 'var(--navbar-height)' : 0};
|
||||||
background-color: ${sidebarBackgroundColor};
|
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {
|
|||||||
SendMessageShortcut,
|
SendMessageShortcut,
|
||||||
setSendMessageShortcut as _setSendMessageShortcut,
|
setSendMessageShortcut as _setSendMessageShortcut,
|
||||||
setTheme,
|
setTheme,
|
||||||
|
setWindowStyle,
|
||||||
ThemeMode
|
ThemeMode
|
||||||
} from '@renderer/store/settings'
|
} from '@renderer/store/settings'
|
||||||
|
|
||||||
@ -17,6 +18,9 @@ export function useSettings() {
|
|||||||
},
|
},
|
||||||
setTheme(theme: ThemeMode) {
|
setTheme(theme: ThemeMode) {
|
||||||
dispatch(setTheme(theme))
|
dispatch(setTheme(theme))
|
||||||
|
},
|
||||||
|
setWindowStyle(windowStyle: 'transparent' | 'opaque') {
|
||||||
|
dispatch(setWindowStyle(windowStyle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -202,6 +202,9 @@ const resources = {
|
|||||||
'theme.dark': 'Dark',
|
'theme.dark': 'Dark',
|
||||||
'theme.light': 'Light',
|
'theme.light': 'Light',
|
||||||
'theme.auto': 'Auto',
|
'theme.auto': 'Auto',
|
||||||
|
'theme.window.style.title': 'Window Style',
|
||||||
|
'theme.window.style.transparent': 'Transparent Window',
|
||||||
|
'theme.window.style.opaque': 'Opaque Window',
|
||||||
'font_size.title': 'Message Font Size'
|
'font_size.title': 'Message Font Size'
|
||||||
},
|
},
|
||||||
translate: {
|
translate: {
|
||||||
@ -444,6 +447,9 @@ const resources = {
|
|||||||
'theme.dark': '深色主题',
|
'theme.dark': '深色主题',
|
||||||
'theme.light': '浅色主题',
|
'theme.light': '浅色主题',
|
||||||
'theme.auto': '跟随系统',
|
'theme.auto': '跟随系统',
|
||||||
|
'theme.window.style.title': '窗口样式',
|
||||||
|
'theme.window.style.transparent': '透明窗口',
|
||||||
|
'theme.window.style.opaque': '不透明窗口',
|
||||||
'font_size.title': '消息字体大小'
|
'font_size.title': '消息字体大小'
|
||||||
},
|
},
|
||||||
translate: {
|
translate: {
|
||||||
|
|||||||
@ -106,7 +106,7 @@ const Assistants: FC<Props> = ({
|
|||||||
|
|
||||||
if (showTopics) {
|
if (showTopics) {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container style={{ padding: 0 }}>
|
||||||
<Topics assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
<Topics assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
@ -155,6 +155,7 @@ const AssistantItem = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
|
padding-right: 35px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: Ubuntu;
|
font-family: Ubuntu;
|
||||||
.anticon {
|
.anticon {
|
||||||
|
|||||||
@ -64,7 +64,7 @@ const HomePage: FC = () => {
|
|||||||
{showAssistants && (
|
{showAssistants && (
|
||||||
<NavbarLeft
|
<NavbarLeft
|
||||||
style={{ justifyContent: 'space-between', alignItems: 'center', borderRight: 'none', padding: '0 8px' }}>
|
style={{ justifyContent: 'space-between', alignItems: 'center', borderRight: 'none', padding: '0 8px' }}>
|
||||||
<NavigtaionBack onClick={() => setShowTopics(false)}>
|
<NavigtaionBack className={showTopics ? 'back' : ''} onClick={() => setShowTopics(false)}>
|
||||||
{showTopics && <ArrowLeftOutlined />}
|
{showTopics && <ArrowLeftOutlined />}
|
||||||
<NavigationBackTitle>{showTopics ? t('common.back') : t('common.chat')}</NavigationBackTitle>
|
<NavigationBackTitle>{showTopics ? t('common.back') : t('common.chat')}</NavigationBackTitle>
|
||||||
</NavigtaionBack>
|
</NavigtaionBack>
|
||||||
@ -124,16 +124,18 @@ const NavigtaionBack = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
cursor: pointer;
|
margin-left: ${isMac ? '10px' : 0};
|
||||||
margin-left: ${isMac ? '14px' : '2px'};
|
|
||||||
-webkit-app-region: none;
|
-webkit-app-region: none;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
padding: 2px 8px;
|
padding: 3px 8px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
&:hover {
|
&.back {
|
||||||
background-color: var(--color-background-mute);
|
cursor: pointer;
|
||||||
color: var(--color-text-1);
|
&:hover {
|
||||||
|
background-color: var(--color-background-mute);
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -172,7 +174,7 @@ export const NewButton = styled.div`
|
|||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-mute);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--color-icon-white);
|
color: var(--color-icon-white);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import { DeleteOutlined, EditOutlined, OpenAIOutlined } from '@ant-design/icons'
|
import { DeleteOutlined, EditOutlined, OpenAIOutlined } from '@ant-design/icons'
|
||||||
import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd'
|
import DragableList from '@renderer/components/DragableList'
|
||||||
import PromptPopup from '@renderer/components/Popups/PromptPopup'
|
import PromptPopup from '@renderer/components/Popups/PromptPopup'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { fetchMessagesSummary } from '@renderer/services/api'
|
import { fetchMessagesSummary } from '@renderer/services/api'
|
||||||
import LocalStorage from '@renderer/services/storage'
|
import LocalStorage from '@renderer/services/storage'
|
||||||
import { useAppSelector } from '@renderer/store'
|
import { useAppSelector } from '@renderer/store'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { droppableReorder } from '@renderer/utils'
|
import { Button, Dropdown, MenuProps } from 'antd'
|
||||||
import { Dropdown, MenuProps } from 'antd'
|
|
||||||
import { FC, useCallback } from 'react'
|
import { FC, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -19,7 +18,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic }) => {
|
const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic }) => {
|
||||||
const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
|
const { assistant, removeTopic, updateTopic, updateTopics, removeAllTopics } = useAssistant(_assistant.id)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const generating = useAppSelector((state) => state.runtime.generating)
|
const generating = useAppSelector((state) => state.runtime.generating)
|
||||||
|
|
||||||
@ -77,17 +76,6 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
[assistant, removeTopic, setActiveTopic, t, updateTopic]
|
[assistant, removeTopic, setActiveTopic, t, updateTopic]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onDragEnd = useCallback(
|
|
||||||
(result: DropResult) => {
|
|
||||||
if (result.destination) {
|
|
||||||
const sourceIndex = result.source.index
|
|
||||||
const destIndex = result.destination.index
|
|
||||||
updateTopics(droppableReorder(assistant.topics, sourceIndex, destIndex))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[assistant.topics, updateTopics]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onSwitchTopic = useCallback(
|
const onSwitchTopic = useCallback(
|
||||||
(topic: Topic) => {
|
(topic: Topic) => {
|
||||||
if (generating) {
|
if (generating) {
|
||||||
@ -99,35 +87,35 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
[generating, setActiveTopic, t]
|
[generating, setActiveTopic, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const onDeleteAll = () => {
|
||||||
|
window.modal.confirm({
|
||||||
|
title: t('chat.topics.delete.all.title'),
|
||||||
|
content: t('chat.topics.delete.all.content'),
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
onOk: removeAllTopics
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragableList list={assistant.topics} onUpdate={updateTopics}>
|
||||||
<Droppable droppableId="droppable">
|
{(topic) => (
|
||||||
{(provided) => (
|
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
|
||||||
<div {...provided.droppableProps} ref={provided.innerRef}>
|
<TopicListItem
|
||||||
{assistant.topics.map((topic, index) => (
|
className={topic.id === activeTopic?.id ? 'active' : ''}
|
||||||
<Draggable key={`draggable_${topic.id}_${index}`} draggableId={topic.id} index={index}>
|
onClick={() => onSwitchTopic(topic)}>
|
||||||
{(provided) => (
|
{topic.name}
|
||||||
<div
|
</TopicListItem>
|
||||||
ref={provided.innerRef}
|
</Dropdown>
|
||||||
{...provided.draggableProps}
|
)}
|
||||||
{...provided.dragHandleProps}
|
</DragableList>
|
||||||
style={{ ...provided.draggableProps.style, marginBottom: 5 }}>
|
{assistant.topics.length > 20 && (
|
||||||
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
|
<Footer>
|
||||||
<TopicListItem
|
<Button style={{ width: '100%' }} onClick={onDeleteAll}>
|
||||||
className={topic.id === activeTopic?.id ? 'active' : ''}
|
{t('chat.topics.delete.all.title')}
|
||||||
onClick={() => onSwitchTopic(topic)}>
|
</Button>
|
||||||
{topic.name}
|
</Footer>
|
||||||
</TopicListItem>
|
)}
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Draggable>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Droppable>
|
|
||||||
</DragDropContext>
|
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -136,14 +124,11 @@ const Container = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: scroll;
|
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
|
||||||
margin-top: -10px;
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const TopicListItem = styled.div`
|
const TopicListItem = styled.div`
|
||||||
padding: 7px 10px;
|
padding: 6px 10px;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -151,6 +136,7 @@ const TopicListItem = styled.div`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-family: Ubuntu;
|
font-family: Ubuntu;
|
||||||
|
font-size: 13px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
}
|
}
|
||||||
@ -160,4 +146,10 @@ const TopicListItem = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const Footer = styled.div`
|
||||||
|
padding: 0 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
export default Topics
|
export default Topics
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import { FolderOpenOutlined, SaveOutlined } from '@ant-design/icons'
|
import { FolderOpenOutlined, SaveOutlined } from '@ant-design/icons'
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import i18n from '@renderer/i18n'
|
import i18n from '@renderer/i18n'
|
||||||
import { backup, reset, restore } from '@renderer/services/backup'
|
import { backup, reset, restore } from '@renderer/services/backup'
|
||||||
import LocalStorage from '@renderer/services/storage'
|
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { setAvatar } from '@renderer/store/runtime'
|
|
||||||
import { setLanguage, setUserName, ThemeMode } from '@renderer/store/settings'
|
import { setLanguage, setUserName, ThemeMode } from '@renderer/store/settings'
|
||||||
import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings'
|
import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings'
|
||||||
import { compressImage, isValidProxyUrl } from '@renderer/utils'
|
import { isValidProxyUrl } from '@renderer/utils'
|
||||||
import { Avatar, Button, Input, Select, Upload } from 'antd'
|
import { Avatar, Button, Input, Select } from 'antd'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -18,8 +15,7 @@ import styled from 'styled-components'
|
|||||||
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '.'
|
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '.'
|
||||||
|
|
||||||
const GeneralSettings: FC = () => {
|
const GeneralSettings: FC = () => {
|
||||||
const avatar = useAvatar()
|
const { language, proxyUrl: storeProxyUrl, userName, theme, windowStyle, setTheme, setWindowStyle } = useSettings()
|
||||||
const { language, proxyUrl: storeProxyUrl, userName, theme, setTheme } = useSettings()
|
|
||||||
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
|
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -72,24 +68,16 @@ const GeneralSettings: FC = () => {
|
|||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('common.avatar')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.theme.window.style.title')}</SettingRowTitle>
|
||||||
<Upload
|
<Select
|
||||||
customRequest={() => {}}
|
defaultValue={windowStyle || 'opaque'}
|
||||||
accept="image/png, image/jpeg"
|
style={{ width: 120 }}
|
||||||
itemRender={() => null}
|
onChange={setWindowStyle}
|
||||||
maxCount={1}
|
options={[
|
||||||
onChange={async ({ file }) => {
|
{ value: 'transparent', label: t('settings.theme.window.style.transparent') },
|
||||||
try {
|
{ value: 'opaque', label: t('settings.theme.window.style.opaque') }
|
||||||
const _file = file.originFileObj as File
|
]}
|
||||||
const compressedFile = await compressImage(_file)
|
/>
|
||||||
await LocalStorage.storeImage('avatar', compressedFile)
|
|
||||||
dispatch(setAvatar(await LocalStorage.getImage('avatar')))
|
|
||||||
} catch (error: any) {
|
|
||||||
window.message.error(error.message)
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<UserAvatar src={avatar} size="large" />
|
|
||||||
</Upload>
|
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
@ -98,7 +86,7 @@ const GeneralSettings: FC = () => {
|
|||||||
placeholder={t('settings.general.user_name.placeholder')}
|
placeholder={t('settings.general.user_name.placeholder')}
|
||||||
value={userName}
|
value={userName}
|
||||||
onChange={(e) => dispatch(setUserName(e.target.value))}
|
onChange={(e) => dispatch(setUserName(e.target.value))}
|
||||||
style={{ width: 150 }}
|
style={{ width: 170 }}
|
||||||
maxLength={30}
|
maxLength={30}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
@ -109,7 +97,7 @@ const GeneralSettings: FC = () => {
|
|||||||
placeholder="socks5://127.0.0.1:6153"
|
placeholder="socks5://127.0.0.1:6153"
|
||||||
value={proxyUrl}
|
value={proxyUrl}
|
||||||
onChange={(e) => setProxyUrl(e.target.value)}
|
onChange={(e) => setProxyUrl(e.target.value)}
|
||||||
style={{ width: 300 }}
|
style={{ width: 170 }}
|
||||||
onBlur={() => onSetProxyUrl()}
|
onBlur={() => onSetProxyUrl()}
|
||||||
type="url"
|
type="url"
|
||||||
/>
|
/>
|
||||||
@ -117,7 +105,7 @@ const GeneralSettings: FC = () => {
|
|||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('settings.general.backup.title')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.general.backup.title')}</SettingRowTitle>
|
||||||
<HStack gap="5px">
|
<HStack gap="5px" w="170px" justifyContent="space-between">
|
||||||
<Button onClick={backup} icon={<SaveOutlined />}>
|
<Button onClick={backup} icon={<SaveOutlined />}>
|
||||||
{t('settings.general.backup.button')}
|
{t('settings.general.backup.button')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export interface SettingsState {
|
|||||||
messageFont: 'system' | 'serif'
|
messageFont: 'system' | 'serif'
|
||||||
showInputEstimatedTokens: boolean
|
showInputEstimatedTokens: boolean
|
||||||
theme: ThemeMode
|
theme: ThemeMode
|
||||||
|
windowStyle: 'transparent' | 'opaque'
|
||||||
fontSize: number
|
fontSize: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ const initialState: SettingsState = {
|
|||||||
messageFont: 'system',
|
messageFont: 'system',
|
||||||
showInputEstimatedTokens: false,
|
showInputEstimatedTokens: false,
|
||||||
theme: ThemeMode.light,
|
theme: ThemeMode.light,
|
||||||
|
windowStyle: 'opaque',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +69,10 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setFontSize: (state, action: PayloadAction<number>) => {
|
setFontSize: (state, action: PayloadAction<number>) => {
|
||||||
state.fontSize = action.payload
|
state.fontSize = action.payload
|
||||||
|
},
|
||||||
|
setWindowStyle: (state, action: PayloadAction<'transparent' | 'opaque'>) => {
|
||||||
|
state.windowStyle = action.payload
|
||||||
|
console.log(state.windowStyle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -81,7 +87,8 @@ export const {
|
|||||||
setMessageFont,
|
setMessageFont,
|
||||||
setShowInputEstimatedTokens,
|
setShowInputEstimatedTokens,
|
||||||
setTheme,
|
setTheme,
|
||||||
setFontSize
|
setFontSize,
|
||||||
|
setWindowStyle
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user