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