feat: add dark and light theme
This commit is contained in:
parent
b91081ef99
commit
50f08124d7
@ -26,6 +26,7 @@
|
|||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
"@sentry/electron": "^5.2.0",
|
"@sentry/electron": "^5.2.0",
|
||||||
"electron-log": "^5.1.5",
|
"electron-log": "^5.1.5",
|
||||||
|
"electron-store": "^8.2.0",
|
||||||
"electron-updater": "^6.1.7",
|
"electron-updater": "^6.1.7",
|
||||||
"electron-window-state": "^5.0.3"
|
"electron-window-state": "^5.0.3"
|
||||||
},
|
},
|
||||||
|
|||||||
15
src/main/config.ts
Normal file
15
src/main/config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Store from 'electron-store'
|
||||||
|
|
||||||
|
export const appConfig = new Store()
|
||||||
|
|
||||||
|
export const titleBarOverlayDark = {
|
||||||
|
height: 41,
|
||||||
|
color: '#1f1f1f',
|
||||||
|
symbolColor: '#ffffff'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const titleBarOverlayLight = {
|
||||||
|
height: 41,
|
||||||
|
color: '#f8f8f8',
|
||||||
|
symbolColor: '#000000'
|
||||||
|
}
|
||||||
@ -5,8 +5,9 @@ import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'
|
|||||||
import windowStateKeeper from 'electron-window-state'
|
import windowStateKeeper from 'electron-window-state'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import icon from '../../resources/icon.png?asset'
|
import icon from '../../resources/icon.png?asset'
|
||||||
import AppUpdater from './updater'
|
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
|
||||||
import { saveFile } from './event'
|
import { saveFile } from './event'
|
||||||
|
import AppUpdater from './updater'
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
// Load the previous state with fallback to defaults
|
// Load the previous state with fallback to defaults
|
||||||
@ -15,6 +16,8 @@ function createWindow() {
|
|||||||
defaultHeight: 670
|
defaultHeight: 670
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const theme = appConfig.get('theme') || 'light'
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
@ -26,11 +29,7 @@ function createWindow() {
|
|||||||
show: true,
|
show: true,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
titleBarStyle: 'hidden',
|
titleBarStyle: 'hidden',
|
||||||
titleBarOverlay: {
|
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
|
||||||
height: 41,
|
|
||||||
color: '#1f1f1f',
|
|
||||||
symbolColor: '#eee'
|
|
||||||
},
|
|
||||||
trafficLightPosition: { x: 8, y: 12 },
|
trafficLightPosition: { x: 8, y: 12 },
|
||||||
...(process.platform === 'linux' ? { icon } : {}),
|
...(process.platform === 'linux' ? { icon } : {}),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
@ -118,6 +117,12 @@ app.whenReady().then(() => {
|
|||||||
|
|
||||||
ipcMain.handle('save-file', saveFile)
|
ipcMain.handle('save-file', saveFile)
|
||||||
|
|
||||||
|
ipcMain.handle('set-theme', (_, theme: 'light' | 'dark') => {
|
||||||
|
appConfig.set('theme', theme)
|
||||||
|
mainWindow?.setTitleBarOverlay &&
|
||||||
|
mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight)
|
||||||
|
})
|
||||||
|
|
||||||
// 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法)
|
// 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法)
|
||||||
ipcMain.handle('check-for-update', async () => {
|
ipcMain.handle('check-for-update', async () => {
|
||||||
autoUpdater.logger?.info('触发检查更新')
|
autoUpdater.logger?.info('触发检查更新')
|
||||||
|
|||||||
1
src/preload/index.d.ts
vendored
1
src/preload/index.d.ts
vendored
@ -12,6 +12,7 @@ declare global {
|
|||||||
openWebsite: (url: string) => void
|
openWebsite: (url: string) => void
|
||||||
setProxy: (proxy: string | undefined) => void
|
setProxy: (proxy: string | undefined) => void
|
||||||
saveFile: (path: string, content: string) => void
|
saveFile: (path: string, content: string) => void
|
||||||
|
setTheme: (theme: 'light' | 'dark') => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,8 @@ const api = {
|
|||||||
checkForUpdate: () => ipcRenderer.invoke('check-for-update'),
|
checkForUpdate: () => ipcRenderer.invoke('check-for-update'),
|
||||||
openWebsite: (url: string) => ipcRenderer.invoke('open-website', url),
|
openWebsite: (url: string) => ipcRenderer.invoke('open-website', url),
|
||||||
setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy),
|
setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy),
|
||||||
saveFile: (path: string, content: string) => ipcRenderer.invoke('save-file', path, content)
|
saveFile: (path: string, content: string) => ipcRenderer.invoke('save-file', path, content),
|
||||||
|
setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use `contextBridge` APIs to expose Electron APIs to
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
|
|||||||
@ -4,13 +4,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Cherry Studio</title>
|
<title>Cherry Studio</title>
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data:" />
|
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data:" />
|
||||||
</head>
|
</head>
|
||||||
|
<body>
|
||||||
<body theme-mode="dark">
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
import store, { persistor } from '@renderer/store'
|
import store, { persistor } from '@renderer/store'
|
||||||
import { ConfigProvider } from 'antd'
|
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { HashRouter, Route, Routes } from 'react-router-dom'
|
import { HashRouter, Route, Routes } from 'react-router-dom'
|
||||||
import { PersistGate } from 'redux-persist/integration/react'
|
import { PersistGate } from 'redux-persist/integration/react'
|
||||||
import Sidebar from './components/app/Sidebar'
|
import Sidebar from './components/app/Sidebar'
|
||||||
import TopViewContainer from './components/TopView'
|
import TopViewContainer from './components/TopView'
|
||||||
import { AntdThemeConfig, getAntdLocale } from './config/antd'
|
|
||||||
import AppsPage from './pages/apps/AppsPage'
|
import AppsPage from './pages/apps/AppsPage'
|
||||||
import HomePage from './pages/home/HomePage'
|
import HomePage from './pages/home/HomePage'
|
||||||
import SettingsPage from './pages/settings/SettingsPage'
|
import SettingsPage from './pages/settings/SettingsPage'
|
||||||
import TranslatePage from './pages/translate/TranslatePage'
|
import TranslatePage from './pages/translate/TranslatePage'
|
||||||
|
import AntdProvider from './providers/AntdProvider'
|
||||||
|
import { ThemeProvider } from './providers/ThemeProvider'
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<ConfigProvider theme={AntdThemeConfig} locale={getAntdLocale()}>
|
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
<ThemeProvider>
|
||||||
|
<AntdProvider>
|
||||||
<PersistGate loading={null} persistor={persistor}>
|
<PersistGate loading={null} persistor={persistor}>
|
||||||
<TopViewContainer>
|
<TopViewContainer>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
@ -28,8 +29,9 @@ function App(): JSX.Element {
|
|||||||
</HashRouter>
|
</HashRouter>
|
||||||
</TopViewContainer>
|
</TopViewContainer>
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
|
</AntdProvider>
|
||||||
|
</ThemeProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</ConfigProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4563475 */
|
font-family: "iconfont"; /* Project id 4563475 */
|
||||||
src: url('iconfont.woff2?t=1722099305424') format('woff2'),
|
src: url('iconfont.woff2?t=1722242729348') format('woff2'),
|
||||||
url('iconfont.woff?t=1722099305424') format('woff'),
|
url('iconfont.woff?t=1722242729348') format('woff'),
|
||||||
url('iconfont.ttf?t=1722099305424') format('truetype');
|
url('iconfont.ttf?t=1722242729348') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -13,6 +13,14 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-dark1:before {
|
||||||
|
content: "\e72f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-theme-light:before {
|
||||||
|
content: "\e6b7";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-translate_line:before {
|
.icon-translate_line:before {
|
||||||
content: "\e7de";
|
content: "\e7de";
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,11 +2,6 @@
|
|||||||
@import './markdown.scss';
|
@import './markdown.scss';
|
||||||
@import './scrollbar.scss';
|
@import './scrollbar.scss';
|
||||||
|
|
||||||
// @font-face {
|
|
||||||
// font-family: 'Playwrite';
|
|
||||||
// src: url(../fonts/Playwrite.ttf) format('truetype');
|
|
||||||
// }
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-white: #ffffff;
|
--color-white: #ffffff;
|
||||||
--color-white-soft: #f8f8f8;
|
--color-white-soft: #f8f8f8;
|
||||||
@ -28,17 +23,22 @@
|
|||||||
--color-background-soft: var(--color-black-soft);
|
--color-background-soft: var(--color-black-soft);
|
||||||
--color-background-mute: var(--color-black-mute);
|
--color-background-mute: var(--color-black-mute);
|
||||||
|
|
||||||
--color-primary: #00b96b;
|
--color-primary: #135200;
|
||||||
--color-primary-soft: #00b96b99;
|
--color-primary-soft: #13520099;
|
||||||
--color-primary-mute: #00b96b33;
|
--color-primary-mute: #13520033;
|
||||||
|
|
||||||
--color-text: var(--color-text-1);
|
--color-text: var(--color-text-1);
|
||||||
--color-icon: #ffffff99;
|
--color-icon: #ffffff99;
|
||||||
--color-icon-white: #ffffff;
|
--color-icon-white: #ffffff;
|
||||||
--color-border: #ffffff20;
|
--color-border: #ffffff20;
|
||||||
--color-error: #f44336;
|
--color-error: #f44336;
|
||||||
|
--color-code-background: #323232;
|
||||||
|
--color-scrollbar-thumb: rgba(255, 255, 255, 0.15);
|
||||||
|
--color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
|
||||||
|
|
||||||
--navbar-background: #1f1f1f;
|
--navbar-background: #1f1f1f;
|
||||||
|
--sidebar-background: #1f1f1f;
|
||||||
|
|
||||||
--navbar-height: 42px;
|
--navbar-height: 42px;
|
||||||
--sidebar-width: 55px;
|
--sidebar-width: 55px;
|
||||||
--assistants-width: 245px;
|
--assistants-width: 245px;
|
||||||
@ -48,6 +48,44 @@
|
|||||||
--input-bar-height: 125px;
|
--input-bar-height: 125px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[theme-mode='light'] {
|
||||||
|
--color-white: #ffffff;
|
||||||
|
--color-white-soft: #f8f8f8;
|
||||||
|
--color-white-mute: #efefef;
|
||||||
|
|
||||||
|
--color-black: #1b1b1f;
|
||||||
|
--color-black-soft: #262626;
|
||||||
|
--color-black-mute: #363636;
|
||||||
|
|
||||||
|
--color-gray-1: #8e8e93;
|
||||||
|
--color-gray-2: #aeaeb2;
|
||||||
|
--color-gray-3: #c7c7cc;
|
||||||
|
|
||||||
|
--color-text-1: rgba(0, 0, 0, 1);
|
||||||
|
--color-text-2: rgba(0, 0, 0, 0.6);
|
||||||
|
--color-text-3: rgba(0, 0, 0, 0.38);
|
||||||
|
|
||||||
|
--color-background: #ffffff;
|
||||||
|
--color-background-soft: var(--color-white-soft);
|
||||||
|
--color-background-mute: var(--color-white-mute);
|
||||||
|
|
||||||
|
--color-primary: #00b96b;
|
||||||
|
--color-primary-soft: #00b96b99;
|
||||||
|
--color-primary-mute: #00b96b33;
|
||||||
|
|
||||||
|
--color-text: var(--color-text-1);
|
||||||
|
--color-icon: #00000099;
|
||||||
|
--color-icon-white: #000000;
|
||||||
|
--color-border: #00000028;
|
||||||
|
--color-error: #f44336;
|
||||||
|
--color-code-background: #e3e3e3;
|
||||||
|
--color-scrollbar-thumb: rgba(0, 0, 0, 0.15);
|
||||||
|
--color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
--navbar-background: #f8f8f8;
|
||||||
|
--sidebar-background: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
*::after {
|
*::after {
|
||||||
@ -68,8 +106,18 @@ body {
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans',
|
font-family:
|
||||||
'Droid Sans', 'Helvetica Neue', sans-serif;
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Microsoft YaHei',
|
||||||
|
'Segoe UI',
|
||||||
|
Roboto,
|
||||||
|
Oxygen,
|
||||||
|
Ubuntu,
|
||||||
|
Cantarell,
|
||||||
|
'Fira Sans',
|
||||||
|
'Droid Sans',
|
||||||
|
'Helvetica Neue' sans-serif;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
@ -97,3 +145,9 @@ body,
|
|||||||
#inputbar .ant-input {
|
#inputbar .ant-input {
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-nav-dropdown {
|
||||||
|
.ant-dropdown-menu {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
.markdown {
|
.markdown {
|
||||||
color: #f1f1f1;
|
color: var(--color-text);
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
@ -33,44 +33,36 @@
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding-left: 1.5em;
|
padding-left: 1.5em;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
color: #ccc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
|||||||
@ -8,8 +8,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: rgba(255, 255, 255, 0.15);
|
background: var(--color-scrollbar-thumb);
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: var(--color-scrollbar-thumb-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ const Container = styled.div`
|
|||||||
min-width: var(--sidebar-width);
|
min-width: var(--sidebar-width);
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
-webkit-app-region: drag !important;
|
-webkit-app-region: drag !important;
|
||||||
background-color: #1f1f1f;
|
background-color: var(--sidebar-background);
|
||||||
border-right: 0.5px solid var(--color-border);
|
border-right: 0.5px solid var(--color-border);
|
||||||
padding-top: var(--navbar-height);
|
padding-top: var(--navbar-height);
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -68,7 +68,7 @@ const AvatarImg = styled.img`
|
|||||||
height: 28px;
|
height: 28px;
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
margin-top: ${isMac ? '16px' : '7px'};
|
margin-top: ${isMac ? '16px' : '9px'};
|
||||||
`
|
`
|
||||||
const MainMenus = styled.div`
|
const MainMenus = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -102,7 +102,7 @@ const Icon = styled.div`
|
|||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #ffffff30;
|
background-color: var(--color-background-soft);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.iconfont,
|
.iconfont,
|
||||||
.anticon {
|
.anticon {
|
||||||
@ -110,7 +110,7 @@ const Icon = styled.div`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
background-color: #ffffff20;
|
background-color: var(--color-background-mute);
|
||||||
.iconfont,
|
.iconfont,
|
||||||
.anticon {
|
.anticon {
|
||||||
color: var(--color-icon-white);
|
color: var(--color-icon-white);
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
import store from '@renderer/store'
|
|
||||||
import { theme, ThemeConfig } from 'antd'
|
|
||||||
import zhCN from 'antd/locale/zh_CN'
|
|
||||||
|
|
||||||
export const colorPrimary = '#00b96b'
|
|
||||||
|
|
||||||
export const AntdThemeConfig: ThemeConfig = {
|
|
||||||
token: {
|
|
||||||
colorPrimary,
|
|
||||||
borderRadius: 5
|
|
||||||
},
|
|
||||||
algorithm: [theme.darkAlgorithm]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAntdLocale() {
|
|
||||||
const language = store.getState().settings.language
|
|
||||||
|
|
||||||
switch (language) {
|
|
||||||
case 'zh-CN':
|
|
||||||
return zhCN
|
|
||||||
case 'en-US':
|
|
||||||
return undefined
|
|
||||||
default:
|
|
||||||
return zhCN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,10 @@
|
|||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { setSendMessageShortcut as _setSendMessageShortcut, SendMessageShortcut } from '@renderer/store/settings'
|
import {
|
||||||
|
setSendMessageShortcut as _setSendMessageShortcut,
|
||||||
|
SendMessageShortcut,
|
||||||
|
setTheme,
|
||||||
|
ThemeMode
|
||||||
|
} from '@renderer/store/settings'
|
||||||
|
|
||||||
export function useSettings() {
|
export function useSettings() {
|
||||||
const settings = useAppSelector((state) => state.settings)
|
const settings = useAppSelector((state) => state.settings)
|
||||||
@ -9,6 +14,9 @@ export function useSettings() {
|
|||||||
...settings,
|
...settings,
|
||||||
setSendMessageShortcut(shortcut: SendMessageShortcut) {
|
setSendMessageShortcut(shortcut: SendMessageShortcut) {
|
||||||
dispatch(_setSendMessageShortcut(shortcut))
|
dispatch(_setSendMessageShortcut(shortcut))
|
||||||
|
},
|
||||||
|
setTheme(theme: ThemeMode) {
|
||||||
|
dispatch(setTheme(theme))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -156,7 +156,11 @@ const resources = {
|
|||||||
'about.feedback.button': 'Feedback',
|
'about.feedback.button': 'Feedback',
|
||||||
'about.contact.title': '📧 Contact',
|
'about.contact.title': '📧 Contact',
|
||||||
'about.contact.button': 'Email',
|
'about.contact.button': 'Email',
|
||||||
'proxy.title': 'Proxy Address'
|
'proxy.title': 'Proxy Address',
|
||||||
|
'theme.title': 'Theme',
|
||||||
|
'theme.dark': 'Dark',
|
||||||
|
'theme.light': 'Light',
|
||||||
|
'theme.auto': 'Auto'
|
||||||
},
|
},
|
||||||
translate: {
|
translate: {
|
||||||
title: 'Translation',
|
title: 'Translation',
|
||||||
@ -334,7 +338,11 @@ const resources = {
|
|||||||
'about.feedback.button': '反馈',
|
'about.feedback.button': '反馈',
|
||||||
'about.contact.title': '📧 邮件联系',
|
'about.contact.title': '📧 邮件联系',
|
||||||
'about.contact.button': '邮件',
|
'about.contact.button': '邮件',
|
||||||
'proxy.title': '代理地址'
|
'proxy.title': '代理地址',
|
||||||
|
'theme.title': '主题',
|
||||||
|
'theme.dark': '深色主题',
|
||||||
|
'theme.light': '浅色主题',
|
||||||
|
'theme.auto': '跟随系统'
|
||||||
},
|
},
|
||||||
translate: {
|
translate: {
|
||||||
title: '翻译',
|
title: '翻译',
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import localforage from 'localforage'
|
|||||||
import KeyvStorage from '@kangfenmao/keyv-storage'
|
import KeyvStorage from '@kangfenmao/keyv-storage'
|
||||||
import * as Sentry from '@sentry/electron/renderer'
|
import * as Sentry from '@sentry/electron/renderer'
|
||||||
import { isProduction, loadScript } from './utils'
|
import { isProduction, loadScript } from './utils'
|
||||||
|
import { ThemeMode } from './store/settings'
|
||||||
|
|
||||||
async function initSentry() {
|
async function initSentry() {
|
||||||
if (await isProduction()) {
|
if (await isProduction()) {
|
||||||
@ -21,12 +22,12 @@ async function initSentry() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initMermaid() {
|
export async function initMermaid(theme: ThemeMode) {
|
||||||
if (!window.mermaid) {
|
if (!window.mermaid) {
|
||||||
await loadScript('https://unpkg.com/mermaid@10.9.1/dist/mermaid.min.js')
|
await loadScript('https://unpkg.com/mermaid@10.9.1/dist/mermaid.min.js')
|
||||||
window.mermaid.initialize({
|
window.mermaid.initialize({
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
theme: 'dark',
|
theme: theme === ThemeMode.dark ? 'dark' : 'default',
|
||||||
securityLevel: 'loose'
|
securityLevel: 'loose'
|
||||||
})
|
})
|
||||||
window.mermaid.contentLoaded()
|
window.mermaid.contentLoaded()
|
||||||
|
|||||||
@ -116,12 +116,16 @@ const AssistantCard = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
background-color: #111;
|
background-color: var(--color-background-soft);
|
||||||
border: 0.5px solid #151515;
|
border: 0.5px solid var(--color-border);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-mute);
|
||||||
|
}
|
||||||
`
|
`
|
||||||
const EmojiHeader = styled.div`
|
const EmojiHeader = styled.div`
|
||||||
width: 25px;
|
width: 25px;
|
||||||
@ -148,7 +152,7 @@ const AssistantName = styled(Title)`
|
|||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: #fff;
|
color: var(--color-white);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import { useShowAssistants, useShowRightSidebar } from '@renderer/hooks/useStore
|
|||||||
import Navigation from './components/NavigationCenter'
|
import Navigation from './components/NavigationCenter'
|
||||||
import { isMac, isWindows } from '@renderer/config/constant'
|
import { isMac, isWindows } from '@renderer/config/constant'
|
||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
|
import { useTheme } from '@renderer/providers/ThemeProvider'
|
||||||
|
import { Switch } from 'antd'
|
||||||
|
|
||||||
let _activeAssistant: Assistant
|
let _activeAssistant: Assistant
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ const HomePage: FC = () => {
|
|||||||
const { rightSidebarShown, toggleRightSidebar } = useShowRightSidebar()
|
const { rightSidebarShown, toggleRightSidebar } = useShowRightSidebar()
|
||||||
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
||||||
const { defaultAssistant } = useDefaultAssistant()
|
const { defaultAssistant } = useDefaultAssistant()
|
||||||
|
const { theme, toggleTheme } = useTheme()
|
||||||
|
|
||||||
_activeAssistant = activeAssistant
|
_activeAssistant = activeAssistant
|
||||||
|
|
||||||
@ -42,6 +45,12 @@ const HomePage: FC = () => {
|
|||||||
)}
|
)}
|
||||||
<Navigation activeAssistant={activeAssistant} />
|
<Navigation activeAssistant={activeAssistant} />
|
||||||
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 8 }}>
|
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 8 }}>
|
||||||
|
<ThemeSwitch
|
||||||
|
checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
|
||||||
|
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}
|
||||||
|
defaultChecked={theme === 'dark'}
|
||||||
|
onChange={toggleTheme}
|
||||||
|
/>
|
||||||
<NewButton onClick={toggleRightSidebar}>
|
<NewButton onClick={toggleRightSidebar}>
|
||||||
<i className={`iconfont ${rightSidebarShown ? 'icon-showsidebarhoriz' : 'icon-hidesidebarhoriz'}`} />
|
<i className={`iconfont ${rightSidebarShown ? 'icon-showsidebarhoriz' : 'icon-hidesidebarhoriz'}`} />
|
||||||
</NewButton>
|
</NewButton>
|
||||||
@ -101,4 +110,12 @@ export const NewButton = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ThemeSwitch = styled(Switch)`
|
||||||
|
-webkit-app-region: none;
|
||||||
|
margin-right: 8px;
|
||||||
|
.icon-theme {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default HomePage
|
export default HomePage
|
||||||
|
|||||||
@ -96,7 +96,9 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
<AssistantItem
|
<AssistantItem
|
||||||
onClick={() => onSwitchAssistant(assistant)}
|
onClick={() => onSwitchAssistant(assistant)}
|
||||||
className={assistant.id === activeAssistant?.id ? 'active' : ''}>
|
className={assistant.id === activeAssistant?.id ? 'active' : ''}>
|
||||||
<AssistantName>{assistant.name || t('assistant.default.name')}</AssistantName>
|
<AssistantName className="name">
|
||||||
|
{assistant.name || t('assistant.default.name')}
|
||||||
|
</AssistantName>
|
||||||
</AssistantItem>
|
</AssistantItem>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
@ -143,13 +145,15 @@ const AssistantItem = styled.div`
|
|||||||
&.active {
|
&.active {
|
||||||
background-color: var(--color-background-mute);
|
background-color: var(--color-background-mute);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
.name {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const AssistantName = styled.div`
|
const AssistantName = styled.div`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--color-text-1);
|
color: var(--color-text);
|
||||||
font-weight: 500;
|
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import React from 'react'
|
import { CheckOutlined, CopyOutlined } from '@ant-design/icons'
|
||||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
||||||
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
import { CopyOutlined } from '@ant-design/icons'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import Mermaid from './Mermaid'
|
|
||||||
import { initMermaid } from '@renderer/init'
|
import { initMermaid } from '@renderer/init'
|
||||||
|
import { useTheme } from '@renderer/providers/ThemeProvider'
|
||||||
|
import { ThemeMode } from '@renderer/store/settings'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
||||||
|
import { atomDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import Mermaid from './Mermaid'
|
||||||
|
|
||||||
interface CodeBlockProps {
|
interface CodeBlockProps {
|
||||||
children: string
|
children: string
|
||||||
@ -15,16 +17,20 @@ interface CodeBlockProps {
|
|||||||
|
|
||||||
const CodeBlock: React.FC<CodeBlockProps> = ({ children, className, ...rest }) => {
|
const CodeBlock: React.FC<CodeBlockProps> = ({ children, className, ...rest }) => {
|
||||||
const match = /language-(\w+)/.exec(className || '')
|
const match = /language-(\w+)/.exec(className || '')
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const onCopy = () => {
|
const onCopy = () => {
|
||||||
navigator.clipboard.writeText(children)
|
navigator.clipboard.writeText(children)
|
||||||
window.message.success({ content: t('message.copied'), key: 'copy-code' })
|
window.message.success({ content: t('message.copied'), key: 'copy-code' })
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match && match[1] === 'mermaid') {
|
if (match && match[1] === 'mermaid') {
|
||||||
initMermaid()
|
initMermaid(theme)
|
||||||
return <Mermaid chart={children} />
|
return <Mermaid chart={children} />
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,12 +38,13 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className, ...rest }) =
|
|||||||
<div>
|
<div>
|
||||||
<CodeHeader>
|
<CodeHeader>
|
||||||
<CodeLanguage>{'<' + match[1].toUpperCase() + '>'}</CodeLanguage>
|
<CodeLanguage>{'<' + match[1].toUpperCase() + '>'}</CodeLanguage>
|
||||||
<CopyOutlined className="copy" onClick={onCopy} />
|
{!copied && <CopyOutlined className="copy" onClick={onCopy} />}
|
||||||
|
{copied && <CheckOutlined style={{ color: 'var(--color-primary)' }} />}
|
||||||
</CodeHeader>
|
</CodeHeader>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
{...rest}
|
{...rest}
|
||||||
language={match[1]}
|
language={match[1]}
|
||||||
style={atomDark}
|
style={theme === ThemeMode.dark ? atomDark : oneLight}
|
||||||
wrapLongLines={true}
|
wrapLongLines={true}
|
||||||
customStyle={{ borderTopLeftRadius: 0, borderTopRightRadius: 0, marginTop: 0 }}>
|
customStyle={{ borderTopLeftRadius: 0, borderTopRightRadius: 0, marginTop: 0 }}>
|
||||||
{String(children).replace(/\n$/, '')}
|
{String(children).replace(/\n$/, '')}
|
||||||
@ -54,10 +61,10 @@ const CodeHeader = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
color: #fff;
|
color: var(--color-text);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: #323232;
|
background-color: var(--color-code-background);
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border-top-left-radius: 8px;
|
border-top-left-radius: 8px;
|
||||||
|
|||||||
@ -248,7 +248,7 @@ const ToolbarButton = styled(Button)`
|
|||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
.anticon {
|
.anticon {
|
||||||
color: white;
|
color: var(--color-text-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -260,7 +260,7 @@ const TextCount = styled.div`
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--color-text-3);
|
color: var(--color-text-3);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background-color: #121212;
|
background-color: var(--color-background-soft);
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-top-left-radius: 7px;
|
border-top-left-radius: 7px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|||||||
@ -1,16 +1,23 @@
|
|||||||
import { CopyOutlined, DeleteOutlined, EditOutlined, MenuOutlined, SaveOutlined, SyncOutlined } from '@ant-design/icons'
|
import {
|
||||||
import Logo from '@renderer/assets/images/logo.png'
|
CheckOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
MenuOutlined,
|
||||||
|
SaveOutlined,
|
||||||
|
SyncOutlined
|
||||||
|
} from '@ant-design/icons'
|
||||||
import { getModelLogo } from '@renderer/config/provider'
|
import { getModelLogo } from '@renderer/config/provider'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { Message } from '@renderer/types'
|
import { Message } from '@renderer/types'
|
||||||
import { firstLetter } from '@renderer/utils'
|
import { firstLetter, removeLeadingEmoji } from '@renderer/utils'
|
||||||
import { Avatar, Dropdown, Tooltip } from 'antd'
|
import { Avatar, Dropdown, Tooltip } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { isEmpty, upperFirst } from 'lodash'
|
import { isEmpty, upperFirst } from 'lodash'
|
||||||
import { FC, useCallback } from 'react'
|
import { FC, useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -31,6 +38,7 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
const { assistant } = useAssistant(message.assistantId)
|
const { assistant } = useAssistant(message.assistantId)
|
||||||
const { userName, showMessageDivider, messageFont } = useSettings()
|
const { userName, showMessageDivider, messageFont } = useSettings()
|
||||||
const { generating } = useRuntime()
|
const { generating } = useRuntime()
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
const isLastMessage = index === 0
|
const isLastMessage = index === 0
|
||||||
const isUserMessage = message.role === 'user'
|
const isUserMessage = message.role === 'user'
|
||||||
@ -39,6 +47,8 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
const onCopy = () => {
|
const onCopy = () => {
|
||||||
navigator.clipboard.writeText(message.content)
|
navigator.clipboard.writeText(message.content)
|
||||||
window.message.success({ content: t('message.copied'), key: 'copy-message' })
|
window.message.success({ content: t('message.copied'), key: 'copy-message' })
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
@ -105,14 +115,14 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
<MessageHeader>
|
<MessageHeader>
|
||||||
<AvatarWrapper>
|
<AvatarWrapper>
|
||||||
{message.role === 'assistant' ? (
|
{message.role === 'assistant' ? (
|
||||||
<Avatar src={message.modelId ? getModelLogo(message.modelId) : Logo} size={35}>
|
<Avatar src={message.modelId ? getModelLogo(message.modelId) : undefined} size={35}>
|
||||||
{firstLetter(message.modelId).toUpperCase()}
|
{firstLetter(assistant?.name).toUpperCase()}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
) : (
|
) : (
|
||||||
<Avatar src={avatar} size={35} />
|
<Avatar src={avatar} size={35} />
|
||||||
)}
|
)}
|
||||||
<UserWrap>
|
<UserWrap>
|
||||||
<UserName>{getUserName()}</UserName>
|
<UserName>{removeLeadingEmoji(getUserName())}</UserName>
|
||||||
<MessageTime>{dayjs(message.createdAt).format('MM/DD HH:mm')}</MessageTime>
|
<MessageTime>{dayjs(message.createdAt).format('MM/DD HH:mm')}</MessageTime>
|
||||||
</UserWrap>
|
</UserWrap>
|
||||||
</AvatarWrapper>
|
</AvatarWrapper>
|
||||||
@ -137,25 +147,26 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
<MenusBar className={`menubar ${isLastMessage && 'show'} ${(!isLastMessage || isUserMessage) && 'user'}`}>
|
<MenusBar className={`menubar ${isLastMessage && 'show'} ${(!isLastMessage || isUserMessage) && 'user'}`}>
|
||||||
{message.role === 'user' && (
|
{message.role === 'user' && (
|
||||||
<Tooltip title="Edit" mouseEnterDelay={0.8}>
|
<Tooltip title="Edit" mouseEnterDelay={0.8}>
|
||||||
<ActionButton>
|
<ActionButton onClick={onEdit}>
|
||||||
<EditOutlined onClick={onEdit} />
|
<EditOutlined />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip title={t('common.copy')} mouseEnterDelay={0.8}>
|
<Tooltip title={t('common.copy')} mouseEnterDelay={0.8}>
|
||||||
<ActionButton>
|
<ActionButton onClick={onCopy}>
|
||||||
<CopyOutlined onClick={onCopy} />
|
{!copied && <CopyOutlined />}
|
||||||
|
{copied && <CheckOutlined style={{ color: 'var(--color-primary)' }} />}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={t('common.delete')} mouseEnterDelay={0.8}>
|
<Tooltip title={t('common.delete')} mouseEnterDelay={0.8}>
|
||||||
<ActionButton>
|
<ActionButton onClick={onDelete}>
|
||||||
<DeleteOutlined onClick={onDelete} />
|
<DeleteOutlined />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{canRegenerate && (
|
{canRegenerate && (
|
||||||
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
||||||
<ActionButton>
|
<ActionButton onClick={onRegenerate}>
|
||||||
<SyncOutlined onClick={onRegenerate} />
|
<SyncOutlined />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { CodeSandboxOutlined } from '@ant-design/icons'
|
import { CodeSandboxOutlined } from '@ant-design/icons'
|
||||||
import { NavbarCenter } from '@renderer/components/app/Navbar'
|
import { NavbarCenter } from '@renderer/components/app/Navbar'
|
||||||
import { colorPrimary } from '@renderer/config/antd'
|
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useProviders } from '@renderer/hooks/useProvider'
|
import { useProviders } from '@renderer/hooks/useProvider'
|
||||||
@ -13,6 +12,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { NewButton } from '../HomePage'
|
import { NewButton } from '../HomePage'
|
||||||
import { getModelLogo } from '@renderer/config/provider'
|
import { getModelLogo } from '@renderer/config/provider'
|
||||||
|
import { removeLeadingEmoji } from '@renderer/utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
activeAssistant: Assistant
|
activeAssistant: Assistant
|
||||||
@ -34,7 +34,7 @@ const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
|
|||||||
children: p.models.map((m) => ({
|
children: p.models.map((m) => ({
|
||||||
key: m.id,
|
key: m.id,
|
||||||
label: upperFirst(m.name),
|
label: upperFirst(m.name),
|
||||||
style: m.id === model?.id ? { color: colorPrimary } : undefined,
|
style: m.id === model?.id ? { color: 'var(--color-primary)' } : undefined,
|
||||||
icon: <Avatar src={getModelLogo(m.id)} size={24} />,
|
icon: <Avatar src={getModelLogo(m.id)} size={24} />,
|
||||||
onClick: () => setModel(m)
|
onClick: () => setModel(m)
|
||||||
}))
|
}))
|
||||||
@ -47,8 +47,11 @@ const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
|
|||||||
<i className="iconfont icon-showsidebarhoriz" />
|
<i className="iconfont icon-showsidebarhoriz" />
|
||||||
</NewButton>
|
</NewButton>
|
||||||
)}
|
)}
|
||||||
<AssistantName>{assistant?.name || t('assistant.default.name')}</AssistantName>
|
<AssistantName>{removeLeadingEmoji(assistant?.name) || t('assistant.default.name')}</AssistantName>
|
||||||
<DropdownMenu menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }} trigger={['click']}>
|
<DropdownMenu
|
||||||
|
menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }}
|
||||||
|
trigger={['click']}
|
||||||
|
overlayClassName="chat-nav-dropdown">
|
||||||
<DropdownButton size="small" type="primary" ghost>
|
<DropdownButton size="small" type="primary" ghost>
|
||||||
<CodeSandboxOutlined />
|
<CodeSandboxOutlined />
|
||||||
<ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName>
|
<ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName>
|
||||||
@ -69,13 +72,14 @@ const AssistantName = styled.span`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const DropdownButton = styled(Button)`
|
const DropdownButton = styled(Button)`
|
||||||
font-size: 10px;
|
font-size: 11px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const ModelName = styled.span`
|
const ModelName = styled.span`
|
||||||
margin-left: -2px;
|
margin-left: -2px;
|
||||||
|
font-weight: bolder;
|
||||||
`
|
`
|
||||||
|
|
||||||
export default NavigationCenter
|
export default NavigationCenter
|
||||||
|
|||||||
@ -90,10 +90,10 @@ const Tab = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #8a8a8a;
|
color: var(--color-text-3);
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
&.active {
|
&.active {
|
||||||
color: #bbb;
|
color: var(--color-text-2);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -130,7 +130,7 @@ const TopicListItem = styled.div`
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -138,7 +138,8 @@ const TopicListItem = styled.div`
|
|||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-mute);
|
||||||
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -8,14 +8,14 @@ import useAvatar from '@renderer/hooks/useAvatar'
|
|||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { setAvatar } from '@renderer/store/runtime'
|
import { setAvatar } from '@renderer/store/runtime'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { setLanguage, setUserName } from '@renderer/store/settings'
|
import { setLanguage, setUserName, ThemeMode } from '@renderer/store/settings'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings'
|
import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings'
|
||||||
import i18n from '@renderer/i18n'
|
import i18n from '@renderer/i18n'
|
||||||
|
|
||||||
const GeneralSettings: FC = () => {
|
const GeneralSettings: FC = () => {
|
||||||
const avatar = useAvatar()
|
const avatar = useAvatar()
|
||||||
const { language, proxyUrl: storeProxyUrl, userName } = 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()
|
||||||
@ -53,6 +53,20 @@ const GeneralSettings: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.theme.title')}</SettingRowTitle>
|
||||||
|
<Select
|
||||||
|
defaultValue={theme}
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={setTheme}
|
||||||
|
options={[
|
||||||
|
{ value: ThemeMode.light, label: t('settings.theme.light') },
|
||||||
|
{ value: ThemeMode.dark, label: t('settings.theme.dark') },
|
||||||
|
{ value: ThemeMode.auto, label: t('settings.theme.auto') }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('common.avatar')}</SettingRowTitle>
|
<SettingRowTitle>{t('common.avatar')}</SettingRowTitle>
|
||||||
<Upload
|
<Upload
|
||||||
|
|||||||
@ -100,11 +100,11 @@ const ProviderSettings: FC = () => {
|
|||||||
key={JSON.stringify(provider)}
|
key={JSON.stringify(provider)}
|
||||||
className={provider.id === selectedProvider?.id ? 'active' : ''}
|
className={provider.id === selectedProvider?.id ? 'active' : ''}
|
||||||
onClick={() => setSelectedProvider(provider)}>
|
onClick={() => setSelectedProvider(provider)}>
|
||||||
{provider.isSystem && <Avatar src={getProviderLogo(provider.id)} size={28} />}
|
{provider.isSystem && <Avatar src={getProviderLogo(provider.id)} size={25} />}
|
||||||
{!provider.isSystem && (
|
{!provider.isSystem && (
|
||||||
<Avatar
|
<Avatar
|
||||||
size={28}
|
size={25}
|
||||||
style={{ backgroundColor: generateColorFromChar(provider.name), minWidth: 28 }}>
|
style={{ backgroundColor: generateColorFromChar(provider.name), minWidth: 25 }}>
|
||||||
{getFirstCharacter(provider.name)}
|
{getFirstCharacter(provider.name)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
)}
|
)}
|
||||||
@ -151,7 +151,7 @@ const ProviderListContainer = styled.div`
|
|||||||
width: var(--assistants-width);
|
width: var(--assistants-width);
|
||||||
height: calc(100vh - var(--navbar-height));
|
height: calc(100vh - var(--navbar-height));
|
||||||
border-right: 0.5px solid var(--color-border);
|
border-right: 0.5px solid var(--color-border);
|
||||||
padding: 10px 8px;
|
padding: 8px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -173,20 +173,21 @@ const ProviderListItem = styled.div`
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #135200;
|
background: var(--color-primary-mute);
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
background: #135200;
|
background: var(--color-primary);
|
||||||
font-weight: bold;
|
color: var(--color-white);
|
||||||
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const ProviderItemName = styled.div`
|
const ProviderItemName = styled.div`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
font-weight: bold;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
font-weight: 500;
|
||||||
`
|
`
|
||||||
|
|
||||||
const AddButtonWrapper = styled.div`
|
const AddButtonWrapper = styled.div`
|
||||||
|
|||||||
@ -68,7 +68,7 @@ const SettingMenus = styled.ul`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-width: var(--assistants-width);
|
min-width: var(--assistants-width);
|
||||||
border-right: 1px solid var(--color-border);
|
border-right: 0.5px solid var(--color-border);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -84,13 +84,14 @@ const MenuItem = styled.li`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #135200;
|
background: var(--color-primary-soft);
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
background: #135200;
|
background: var(--color-primary);
|
||||||
font-weight: bold;
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -178,7 +178,8 @@ const ListHeader = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
color: #ffffff50;
|
color: var(--color-white);
|
||||||
|
opacity: 0.4;
|
||||||
`
|
`
|
||||||
|
|
||||||
const ListItem = styled.div`
|
const ListItem = styled.div`
|
||||||
@ -199,14 +200,14 @@ const ListItemHeader = styled.div`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const ListItemName = styled.div`
|
const ListItemName = styled.div`
|
||||||
color: #fff;
|
color: var(--color-white);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const ModelHeaderTitle = styled.div`
|
const ModelHeaderTitle = styled.div`
|
||||||
color: #fff;
|
color: var(--color-white);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import Link from 'antd/es/typography/Link'
|
|||||||
import { checkApi } from '@renderer/services/api'
|
import { checkApi } from '@renderer/services/api'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { PROVIDER_CONFIG } from '@renderer/config/provider'
|
import { PROVIDER_CONFIG } from '@renderer/config/provider'
|
||||||
|
import { useTheme } from '@renderer/providers/ThemeProvider'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
provider: Provider
|
provider: Provider
|
||||||
@ -33,6 +34,7 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
const [apiChecking, setApiChecking] = useState(false)
|
const [apiChecking, setApiChecking] = useState(false)
|
||||||
const { updateProvider, models, removeModel } = useProvider(provider.id)
|
const { updateProvider, models, removeModel } = useProvider(provider.id)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const modelGroups = groupBy(models, 'group')
|
const modelGroups = groupBy(models, 'group')
|
||||||
|
|
||||||
@ -68,13 +70,18 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingContainer>
|
<SettingContainer
|
||||||
|
style={
|
||||||
|
theme === 'dark'
|
||||||
|
? { backgroundColor: 'var(--color-background)' }
|
||||||
|
: { backgroundColor: 'var(--color-background-mute)' }
|
||||||
|
}>
|
||||||
<SettingTitle>
|
<SettingTitle>
|
||||||
<Flex align="center">
|
<Flex align="center">
|
||||||
<span>{provider.isSystem ? t(`provider.${provider.id}`) : provider.name}</span>
|
<span>{provider.isSystem ? t(`provider.${provider.id}`) : provider.name}</span>
|
||||||
{officialWebsite! && (
|
{officialWebsite! && (
|
||||||
<Link target="_blank" href={providerConfig.websites.official}>
|
<Link target="_blank" href={providerConfig.websites.official}>
|
||||||
<ExportOutlined style={{ marginLeft: '8px', color: 'white', fontSize: '12px' }} />
|
<ExportOutlined style={{ marginLeft: '8px', color: 'var(--color-text)', fontSize: '12px' }} />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -183,7 +190,8 @@ const HelpTextRow = styled.div`
|
|||||||
|
|
||||||
const HelpText = styled.div`
|
const HelpText = styled.div`
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #ffffff50;
|
color: var(--color-text);
|
||||||
|
opacity: 0.4;
|
||||||
`
|
`
|
||||||
|
|
||||||
const HelpLink = styled(Link)`
|
const HelpLink = styled(Link)`
|
||||||
|
|||||||
37
src/renderer/src/providers/AntdProvider.tsx
Normal file
37
src/renderer/src/providers/AntdProvider.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
|
import { ConfigProvider, theme } from 'antd'
|
||||||
|
import zhCN from 'antd/locale/zh_CN'
|
||||||
|
import { FC, PropsWithChildren } from 'react'
|
||||||
|
import { useTheme } from './ThemeProvider'
|
||||||
|
|
||||||
|
const AntdProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||||
|
const { language } = useSettings()
|
||||||
|
const { theme: _theme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfigProvider
|
||||||
|
locale={getAntdLocale(language)}
|
||||||
|
theme={{
|
||||||
|
algorithm: [_theme === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm],
|
||||||
|
token: {
|
||||||
|
colorPrimary: '#00b96b',
|
||||||
|
borderRadius: 5
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAntdLocale(language: string) {
|
||||||
|
switch (language) {
|
||||||
|
case 'zh-CN':
|
||||||
|
return zhCN
|
||||||
|
case 'en-US':
|
||||||
|
return undefined
|
||||||
|
default:
|
||||||
|
return zhCN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AntdProvider
|
||||||
43
src/renderer/src/providers/ThemeProvider.tsx
Normal file
43
src/renderer/src/providers/ThemeProvider.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
|
import { ThemeMode } from '@renderer/store/settings'
|
||||||
|
import React, { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
interface ThemeContextType {
|
||||||
|
theme: ThemeMode
|
||||||
|
toggleTheme: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeContext = createContext<ThemeContextType>({
|
||||||
|
theme: ThemeMode.light,
|
||||||
|
toggleTheme: () => {}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ThemeProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
||||||
|
const { theme, setTheme } = useSettings()
|
||||||
|
const [_theme, _setTheme] = useState(theme)
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
setTheme(theme === ThemeMode.dark ? ThemeMode.light : ThemeMode.dark)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect((): any => {
|
||||||
|
if (theme === ThemeMode.auto) {
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
_setTheme(mediaQuery.matches ? ThemeMode.dark : ThemeMode.light)
|
||||||
|
const handleChange = (e: MediaQueryListEvent) => _setTheme(e.matches ? ThemeMode.dark : ThemeMode.light)
|
||||||
|
mediaQuery.addEventListener('change', handleChange)
|
||||||
|
return () => mediaQuery.removeEventListener('change', handleChange)
|
||||||
|
} else {
|
||||||
|
_setTheme(theme)
|
||||||
|
}
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.setAttribute('theme-mode', _theme)
|
||||||
|
window.api.setTheme(_theme === ThemeMode.dark ? 'dark' : 'light')
|
||||||
|
}, [_theme])
|
||||||
|
|
||||||
|
return <ThemeContext.Provider value={{ theme: _theme, toggleTheme }}>{children}</ThemeContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTheme = () => useContext(ThemeContext)
|
||||||
@ -19,7 +19,7 @@ const persistedReducer = persistReducer(
|
|||||||
{
|
{
|
||||||
key: 'cherry-studio',
|
key: 'cherry-studio',
|
||||||
storage,
|
storage,
|
||||||
version: 16,
|
version: 17,
|
||||||
blacklist: ['runtime'],
|
blacklist: ['runtime'],
|
||||||
migrate
|
migrate
|
||||||
},
|
},
|
||||||
|
|||||||
@ -261,6 +261,15 @@ const migrateConfig = {
|
|||||||
showInputEstimatedTokens: false
|
showInputEstimatedTokens: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'17': (state: RootState) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
settings: {
|
||||||
|
...state.settings,
|
||||||
|
theme: 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
|||||||
|
|
||||||
export type SendMessageShortcut = 'Enter' | 'Shift+Enter'
|
export type SendMessageShortcut = 'Enter' | 'Shift+Enter'
|
||||||
|
|
||||||
|
export enum ThemeMode {
|
||||||
|
light = 'light',
|
||||||
|
dark = 'dark',
|
||||||
|
auto = 'auto'
|
||||||
|
}
|
||||||
|
|
||||||
export interface SettingsState {
|
export interface SettingsState {
|
||||||
showRightSidebar: boolean
|
showRightSidebar: boolean
|
||||||
showAssistants: boolean
|
showAssistants: boolean
|
||||||
@ -12,6 +18,7 @@ export interface SettingsState {
|
|||||||
showMessageDivider: boolean
|
showMessageDivider: boolean
|
||||||
messageFont: 'system' | 'serif'
|
messageFont: 'system' | 'serif'
|
||||||
showInputEstimatedTokens: boolean
|
showInputEstimatedTokens: boolean
|
||||||
|
theme: ThemeMode
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: SettingsState = {
|
const initialState: SettingsState = {
|
||||||
@ -23,7 +30,8 @@ const initialState: SettingsState = {
|
|||||||
userName: '',
|
userName: '',
|
||||||
showMessageDivider: true,
|
showMessageDivider: true,
|
||||||
messageFont: 'system',
|
messageFont: 'system',
|
||||||
showInputEstimatedTokens: false
|
showInputEstimatedTokens: false,
|
||||||
|
theme: ThemeMode.light
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
@ -59,6 +67,9 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setShowInputEstimatedTokens: (state, action: PayloadAction<boolean>) => {
|
setShowInputEstimatedTokens: (state, action: PayloadAction<boolean>) => {
|
||||||
state.showInputEstimatedTokens = action.payload
|
state.showInputEstimatedTokens = action.payload
|
||||||
|
},
|
||||||
|
setTheme: (state, action: PayloadAction<ThemeMode>) => {
|
||||||
|
state.theme = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -73,7 +84,8 @@ export const {
|
|||||||
setUserName,
|
setUserName,
|
||||||
setShowMessageDivider,
|
setShowMessageDivider,
|
||||||
setMessageFont,
|
setMessageFont,
|
||||||
setShowInputEstimatedTokens
|
setShowInputEstimatedTokens,
|
||||||
|
setTheme
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
@ -93,9 +93,14 @@ export function droppableReorder<T>(list: T[], startIndex: number, endIndex: num
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// firstLetter
|
export function firstLetter(str: string): string {
|
||||||
export const firstLetter = (str?: string) => {
|
const match = str.match(/\p{L}\p{M}*|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/u)
|
||||||
return str ? str[0] : ''
|
return match ? match[0] : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeLeadingEmoji(str: string): string {
|
||||||
|
const emojiRegex = /^(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)+/u
|
||||||
|
return str.replace(emojiRegex, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFreeModel(model: Model) {
|
export function isFreeModel(model: Model) {
|
||||||
|
|||||||
207
yarn.lock
207
yarn.lock
@ -2710,6 +2710,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ajv-formats@npm:^2.1.1":
|
||||||
|
version: 2.1.1
|
||||||
|
resolution: "ajv-formats@npm:2.1.1"
|
||||||
|
dependencies:
|
||||||
|
ajv: "npm:^8.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
checksum: 10c0/e43ba22e91b6a48d96224b83d260d3a3a561b42d391f8d3c6d2c1559f9aa5b253bfb306bc94bbeca1d967c014e15a6efe9a207309e95b3eaae07fcbcdc2af662
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ajv-keywords@npm:^3.4.1":
|
"ajv-keywords@npm:^3.4.1":
|
||||||
version: 3.5.2
|
version: 3.5.2
|
||||||
resolution: "ajv-keywords@npm:3.5.2"
|
resolution: "ajv-keywords@npm:3.5.2"
|
||||||
@ -2731,6 +2745,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ajv@npm:^8.0.0, ajv@npm:^8.6.3":
|
||||||
|
version: 8.17.1
|
||||||
|
resolution: "ajv@npm:8.17.1"
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: "npm:^3.1.3"
|
||||||
|
fast-uri: "npm:^3.0.1"
|
||||||
|
json-schema-traverse: "npm:^1.0.0"
|
||||||
|
require-from-string: "npm:^2.0.2"
|
||||||
|
checksum: 10c0/ec3ba10a573c6b60f94639ffc53526275917a2df6810e4ab5a6b959d87459f9ef3f00d5e7865b82677cb7d21590355b34da14d1d0b9c32d75f95a187e76fff35
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ansi-regex@npm:^5.0.1":
|
"ansi-regex@npm:^5.0.1":
|
||||||
version: 5.0.1
|
version: 5.0.1
|
||||||
resolution: "ansi-regex@npm:5.0.1"
|
resolution: "ansi-regex@npm:5.0.1"
|
||||||
@ -3050,6 +3076,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"atomically@npm:^1.7.0":
|
||||||
|
version: 1.7.0
|
||||||
|
resolution: "atomically@npm:1.7.0"
|
||||||
|
checksum: 10c0/31f5efd5d69474681268557af4024f9e10223bb6b39fdedb5f2e19405186c4b76284fac9f6c43c9af75013cad6437e93b7168268f5ddb7aaf1cfc5fdb415f227
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"available-typed-arrays@npm:^1.0.7":
|
"available-typed-arrays@npm:^1.0.7":
|
||||||
version: 1.0.7
|
version: 1.0.7
|
||||||
resolution: "available-typed-arrays@npm:1.0.7"
|
resolution: "available-typed-arrays@npm:1.0.7"
|
||||||
@ -3424,6 +3457,7 @@ __metadata:
|
|||||||
electron-builder: "npm:^24.9.1"
|
electron-builder: "npm:^24.9.1"
|
||||||
electron-devtools-installer: "npm:^3.2.0"
|
electron-devtools-installer: "npm:^3.2.0"
|
||||||
electron-log: "npm:^5.1.5"
|
electron-log: "npm:^5.1.5"
|
||||||
|
electron-store: "npm:^8.2.0"
|
||||||
electron-updater: "npm:^6.1.7"
|
electron-updater: "npm:^6.1.7"
|
||||||
electron-vite: "npm:^2.0.0"
|
electron-vite: "npm:^2.0.0"
|
||||||
electron-window-state: "npm:^5.0.3"
|
electron-window-state: "npm:^5.0.3"
|
||||||
@ -3632,6 +3666,24 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"conf@npm:^10.2.0":
|
||||||
|
version: 10.2.0
|
||||||
|
resolution: "conf@npm:10.2.0"
|
||||||
|
dependencies:
|
||||||
|
ajv: "npm:^8.6.3"
|
||||||
|
ajv-formats: "npm:^2.1.1"
|
||||||
|
atomically: "npm:^1.7.0"
|
||||||
|
debounce-fn: "npm:^4.0.0"
|
||||||
|
dot-prop: "npm:^6.0.1"
|
||||||
|
env-paths: "npm:^2.2.1"
|
||||||
|
json-schema-typed: "npm:^7.0.3"
|
||||||
|
onetime: "npm:^5.1.2"
|
||||||
|
pkg-up: "npm:^3.1.0"
|
||||||
|
semver: "npm:^7.3.5"
|
||||||
|
checksum: 10c0/d608d8c54ba7fad368eac640e77f2ce0334ec27cfd62ac39f44e361af8af9915eaa6c2ada81fbc25c3219273d972b4868bc752e8e2116cb6e12d35df72dc25a4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"config-file-ts@npm:^0.2.4":
|
"config-file-ts@npm:^0.2.4":
|
||||||
version: 0.2.6
|
version: 0.2.6
|
||||||
resolution: "config-file-ts@npm:0.2.6"
|
resolution: "config-file-ts@npm:0.2.6"
|
||||||
@ -3766,6 +3818,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"debounce-fn@npm:^4.0.0":
|
||||||
|
version: 4.0.0
|
||||||
|
resolution: "debounce-fn@npm:4.0.0"
|
||||||
|
dependencies:
|
||||||
|
mimic-fn: "npm:^3.0.0"
|
||||||
|
checksum: 10c0/bcbd8eb253bdb6ee2f32759c95973c62bc479e74efbe1a44e17acfb0ea7d4bcbe615bf7e34aab80247ac08669c1ab72f7da0f384ceb7f15c18333d31d9030384
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
|
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
|
||||||
version: 4.3.4
|
version: 4.3.4
|
||||||
resolution: "debug@npm:4.3.4"
|
resolution: "debug@npm:4.3.4"
|
||||||
@ -3961,6 +4022,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"dot-prop@npm:^6.0.1":
|
||||||
|
version: 6.0.1
|
||||||
|
resolution: "dot-prop@npm:6.0.1"
|
||||||
|
dependencies:
|
||||||
|
is-obj: "npm:^2.0.0"
|
||||||
|
checksum: 10c0/30e51ec6408978a6951b21e7bc4938aad01a86f2fdf779efe52330205c6bb8a8ea12f35925c2029d6dc9d1df22f916f32f828ce1e9b259b1371c580541c22b5a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"dotenv-cli@npm:^7.4.2":
|
"dotenv-cli@npm:^7.4.2":
|
||||||
version: 7.4.2
|
version: 7.4.2
|
||||||
resolution: "dotenv-cli@npm:7.4.2"
|
resolution: "dotenv-cli@npm:7.4.2"
|
||||||
@ -4077,6 +4147,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"electron-store@npm:^8.2.0":
|
||||||
|
version: 8.2.0
|
||||||
|
resolution: "electron-store@npm:8.2.0"
|
||||||
|
dependencies:
|
||||||
|
conf: "npm:^10.2.0"
|
||||||
|
type-fest: "npm:^2.17.0"
|
||||||
|
checksum: 10c0/a4d19827e96ab67bf6c2a375910f51b147b23f4a0468da5cfeeb069acdfdbcd3a9f5650248a62a05aa0967149e4d1c47f2d0ba7582205e5eb38952c93b6882e1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"electron-to-chromium@npm:^1.4.668":
|
"electron-to-chromium@npm:^1.4.668":
|
||||||
version: 1.4.776
|
version: 1.4.776
|
||||||
resolution: "electron-to-chromium@npm:1.4.776"
|
resolution: "electron-to-chromium@npm:1.4.776"
|
||||||
@ -4184,7 +4264,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"env-paths@npm:^2.2.0":
|
"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1":
|
||||||
version: 2.2.1
|
version: 2.2.1
|
||||||
resolution: "env-paths@npm:2.2.1"
|
resolution: "env-paths@npm:2.2.1"
|
||||||
checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4
|
checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4
|
||||||
@ -4809,6 +4889,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"fast-uri@npm:^3.0.1":
|
||||||
|
version: 3.0.1
|
||||||
|
resolution: "fast-uri@npm:3.0.1"
|
||||||
|
checksum: 10c0/3cd46d6006083b14ca61ffe9a05b8eef75ef87e9574b6f68f2e17ecf4daa7aaadeff44e3f0f7a0ef4e0f7e7c20fc07beec49ff14dc72d0b500f00386592f2d10
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"fastq@npm:^1.6.0":
|
"fastq@npm:^1.6.0":
|
||||||
version: 1.17.1
|
version: 1.17.1
|
||||||
resolution: "fastq@npm:1.17.1"
|
resolution: "fastq@npm:1.17.1"
|
||||||
@ -4863,6 +4950,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"find-up@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "find-up@npm:3.0.0"
|
||||||
|
dependencies:
|
||||||
|
locate-path: "npm:^3.0.0"
|
||||||
|
checksum: 10c0/2c2e7d0a26db858e2f624f39038c74739e38306dee42b45f404f770db357947be9d0d587f1cac72d20c114deb38aa57316e879eb0a78b17b46da7dab0a3bd6e3
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"find-up@npm:^5.0.0":
|
"find-up@npm:^5.0.0":
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
resolution: "find-up@npm:5.0.0"
|
resolution: "find-up@npm:5.0.0"
|
||||||
@ -5883,6 +5979,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-obj@npm:^2.0.0":
|
||||||
|
version: 2.0.0
|
||||||
|
resolution: "is-obj@npm:2.0.0"
|
||||||
|
checksum: 10c0/85044ed7ba8bd169e2c2af3a178cacb92a97aa75de9569d02efef7f443a824b5e153eba72b9ae3aca6f8ce81955271aa2dc7da67a8b720575d3e38104208cb4e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-path-inside@npm:^3.0.3":
|
"is-path-inside@npm:^3.0.3":
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
resolution: "is-path-inside@npm:3.0.3"
|
resolution: "is-path-inside@npm:3.0.3"
|
||||||
@ -6135,6 +6238,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"json-schema-traverse@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "json-schema-traverse@npm:1.0.0"
|
||||||
|
checksum: 10c0/71e30015d7f3d6dc1c316d6298047c8ef98a06d31ad064919976583eb61e1018a60a0067338f0f79cabc00d84af3fcc489bd48ce8a46ea165d9541ba17fb30c6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"json-schema-typed@npm:^7.0.3":
|
||||||
|
version: 7.0.3
|
||||||
|
resolution: "json-schema-typed@npm:7.0.3"
|
||||||
|
checksum: 10c0/b4a6d984dd91f9aba72df8768c5ced99e789b8e17b55ee24afb3a687ce55b70a7b3f4360cac67939e1ff98e136ca26f3aa530635c13ef371ae5edc48b69a65f6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"json-stable-stringify-without-jsonify@npm:^1.0.1":
|
"json-stable-stringify-without-jsonify@npm:^1.0.1":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "json-stable-stringify-without-jsonify@npm:1.0.1"
|
resolution: "json-stable-stringify-without-jsonify@npm:1.0.1"
|
||||||
@ -6269,6 +6386,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"locate-path@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "locate-path@npm:3.0.0"
|
||||||
|
dependencies:
|
||||||
|
p-locate: "npm:^3.0.0"
|
||||||
|
path-exists: "npm:^3.0.0"
|
||||||
|
checksum: 10c0/3db394b7829a7fe2f4fbdd25d3c4689b85f003c318c5da4052c7e56eed697da8f1bce5294f685c69ff76e32cba7a33629d94396976f6d05fb7f4c755c5e2ae8b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"locate-path@npm:^6.0.0":
|
"locate-path@npm:^6.0.0":
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
resolution: "locate-path@npm:6.0.0"
|
resolution: "locate-path@npm:6.0.0"
|
||||||
@ -6817,6 +6944,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"mimic-fn@npm:^2.1.0":
|
||||||
|
version: 2.1.0
|
||||||
|
resolution: "mimic-fn@npm:2.1.0"
|
||||||
|
checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"mimic-fn@npm:^3.0.0":
|
||||||
|
version: 3.1.0
|
||||||
|
resolution: "mimic-fn@npm:3.1.0"
|
||||||
|
checksum: 10c0/a07cdd8ed6490c2dff5b11f889b245d9556b80f5a653a552a651d17cff5a2d156e632d235106c2369f00cccef4071704589574cf3601bc1b1400a1f620dff067
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"mimic-response@npm:^1.0.0":
|
"mimic-response@npm:^1.0.0":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "mimic-response@npm:1.0.1"
|
resolution: "mimic-response@npm:1.0.1"
|
||||||
@ -7214,6 +7355,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"onetime@npm:^5.1.2":
|
||||||
|
version: 5.1.2
|
||||||
|
resolution: "onetime@npm:5.1.2"
|
||||||
|
dependencies:
|
||||||
|
mimic-fn: "npm:^2.1.0"
|
||||||
|
checksum: 10c0/ffcef6fbb2692c3c40749f31ea2e22677a876daea92959b8a80b521d95cca7a668c884d8b2045d1d8ee7d56796aa405c405462af112a1477594cc63531baeb8f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"openai-chat-tokens@npm:^0.2.8":
|
"openai-chat-tokens@npm:^0.2.8":
|
||||||
version: 0.2.8
|
version: 0.2.8
|
||||||
resolution: "openai-chat-tokens@npm:0.2.8"
|
resolution: "openai-chat-tokens@npm:0.2.8"
|
||||||
@ -7273,6 +7423,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"p-limit@npm:^2.0.0":
|
||||||
|
version: 2.3.0
|
||||||
|
resolution: "p-limit@npm:2.3.0"
|
||||||
|
dependencies:
|
||||||
|
p-try: "npm:^2.0.0"
|
||||||
|
checksum: 10c0/8da01ac53efe6a627080fafc127c873da40c18d87b3f5d5492d465bb85ec7207e153948df6b9cbaeb130be70152f874229b8242ee2be84c0794082510af97f12
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"p-limit@npm:^3.0.2":
|
"p-limit@npm:^3.0.2":
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
resolution: "p-limit@npm:3.1.0"
|
resolution: "p-limit@npm:3.1.0"
|
||||||
@ -7282,6 +7441,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"p-locate@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "p-locate@npm:3.0.0"
|
||||||
|
dependencies:
|
||||||
|
p-limit: "npm:^2.0.0"
|
||||||
|
checksum: 10c0/7b7f06f718f19e989ce6280ed4396fb3c34dabdee0df948376483032f9d5ec22fdf7077ec942143a75827bb85b11da72016497fc10dac1106c837ed593969ee8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"p-locate@npm:^5.0.0":
|
"p-locate@npm:^5.0.0":
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
resolution: "p-locate@npm:5.0.0"
|
resolution: "p-locate@npm:5.0.0"
|
||||||
@ -7300,6 +7468,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"p-try@npm:^2.0.0":
|
||||||
|
version: 2.2.0
|
||||||
|
resolution: "p-try@npm:2.2.0"
|
||||||
|
checksum: 10c0/c36c19907734c904b16994e6535b02c36c2224d433e01a2f1ab777237f4d86e6289fd5fd464850491e940379d4606ed850c03e0f9ab600b0ebddb511312e177f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"package-json-from-dist@npm:^1.0.0":
|
"package-json-from-dist@npm:^1.0.0":
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
resolution: "package-json-from-dist@npm:1.0.0"
|
resolution: "package-json-from-dist@npm:1.0.0"
|
||||||
@ -7353,6 +7528,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"path-exists@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "path-exists@npm:3.0.0"
|
||||||
|
checksum: 10c0/17d6a5664bc0a11d48e2b2127d28a0e58822c6740bde30403f08013da599182289c56518bec89407e3f31d3c2b6b296a4220bc3f867f0911fee6952208b04167
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"path-exists@npm:^4.0.0":
|
"path-exists@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "path-exists@npm:4.0.0"
|
resolution: "path-exists@npm:4.0.0"
|
||||||
@ -7475,6 +7657,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"pkg-up@npm:^3.1.0":
|
||||||
|
version: 3.1.0
|
||||||
|
resolution: "pkg-up@npm:3.1.0"
|
||||||
|
dependencies:
|
||||||
|
find-up: "npm:^3.0.0"
|
||||||
|
checksum: 10c0/ecb60e1f8e1f611c0bdf1a0b6a474d6dfb51185567dc6f29cdef37c8d480ecba5362e006606bb290519bbb6f49526c403fabea93c3090c20368d98bb90c999ab
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"plist@npm:^3.0.4, plist@npm:^3.0.5":
|
"plist@npm:^3.0.4, plist@npm:^3.0.5":
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
resolution: "plist@npm:3.1.0"
|
resolution: "plist@npm:3.1.0"
|
||||||
@ -8583,6 +8774,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"require-from-string@npm:^2.0.2":
|
||||||
|
version: 2.0.2
|
||||||
|
resolution: "require-from-string@npm:2.0.2"
|
||||||
|
checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"require-in-the-middle@npm:^7.1.1":
|
"require-in-the-middle@npm:^7.1.1":
|
||||||
version: 7.3.0
|
version: 7.3.0
|
||||||
resolution: "require-in-the-middle@npm:7.3.0"
|
resolution: "require-in-the-middle@npm:7.3.0"
|
||||||
@ -9491,6 +9689,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"type-fest@npm:^2.17.0":
|
||||||
|
version: 2.19.0
|
||||||
|
resolution: "type-fest@npm:2.19.0"
|
||||||
|
checksum: 10c0/a5a7ecf2e654251613218c215c7493574594951c08e52ab9881c9df6a6da0aeca7528c213c622bc374b4e0cb5c443aa3ab758da4e3c959783ce884c3194e12cb
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"typed-array-buffer@npm:^1.0.2":
|
"typed-array-buffer@npm:^1.0.2":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "typed-array-buffer@npm:1.0.2"
|
resolution: "typed-array-buffer@npm:1.0.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user