diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 880582c7..6cb2ddce 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -8,6 +8,13 @@ module.exports = {
],
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
- '@typescript-eslint/no-explicit-any': 'off'
+ '@typescript-eslint/no-explicit-any': 'off',
+ 'sort-imports': [
+ 'error',
+ {
+ ignoreCase: true,
+ ignoreDeclarationSort: true
+ }
+ ]
}
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4f6177a3..684fc502 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,7 +1,8 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": "explicit"
+ "source.fixAll.eslint": "explicit",
+ "source.organizeImports": "explicit"
},
"search.exclude": {
"**/dist/**": true
diff --git a/electron.vite.config.ts b/electron.vite.config.ts
index 5b54e205..c454dadf 100644
--- a/electron.vite.config.ts
+++ b/electron.vite.config.ts
@@ -1,6 +1,6 @@
-import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
+import { resolve } from 'path'
export default defineConfig({
main: {
diff --git a/package.json b/package.json
index fcf9c3d2..abb74f5a 100644
--- a/package.json
+++ b/package.json
@@ -26,11 +26,14 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource/inter": "^5.0.18",
+ "@reduxjs/toolkit": "^2.2.5",
"electron-updater": "^6.1.7",
"electron-window-state": "^5.0.3",
"localforage": "^1.10.0",
+ "react-redux": "^9.1.2",
"react-router": "6",
"react-router-dom": "6",
+ "redux-persist": "^6.0.0",
"styled-components": "^6.1.11"
},
"devDependencies": {
diff --git a/src/main/index.ts b/src/main/index.ts
index edd72467..bd30411d 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -1,9 +1,9 @@
-import { app, shell, BrowserWindow, ipcMain } from 'electron'
-import { join } from 'path'
-import { electronApp, optimizer, is } from '@electron-toolkit/utils'
-import icon from '../../resources/icon.png?asset'
+import { app, BrowserWindow, ipcMain, shell } from 'electron'
+import { electronApp, is, optimizer } from '@electron-toolkit/utils'
import windowStateKeeper from 'electron-window-state'
+import { join } from 'path'
import { initStore } from './store'
+import icon from '../../resources/icon.png?asset'
function createWindow(): void {
// Load the previous state with fallback to defaults
diff --git a/src/main/store.ts b/src/main/store.ts
deleted file mode 100644
index 35a39a2d..00000000
--- a/src/main/store.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { app, ipcMain } from 'electron'
-import path from 'path'
-
-const defaultPath = path.join(app.getPath('home'), '.cherry-ai')
-
-export function initStore() {
- ipcMain.on('storage.set', (_, args) => {})
- ipcMain.on('storage.get', (_, args) => {})
- ipcMain.on('storage.delete', (_, args) => {})
- ipcMain.on('storage.clear', (_, args) => {})
-}
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index 3d97e20b..38c58fc3 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -1,22 +1,26 @@
+import '@fontsource/inter'
+import store from '@renderer/store'
+import { Provider } from 'react-redux'
+import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Sidebar from './components/app/Sidebar'
import Statusbar from './components/app/Statusbar'
-import HomePage from './pages/home/HomePage'
-import { BrowserRouter, Routes, Route } from 'react-router-dom'
import AppsPage from './pages/apps/AppsPage'
+import HomePage from './pages/home/HomePage'
import SettingsPage from './pages/settings/SettingsPage'
-import '@fontsource/inter'
function App(): JSX.Element {
return (
-
-
-
- } />
- } />
- } />
-
-
-
+
+
+
+
+ } />
+ } />
+ } />
+
+
+
+
)
}
diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts
new file mode 100644
index 00000000..25cf17af
--- /dev/null
+++ b/src/renderer/src/store/index.ts
@@ -0,0 +1,19 @@
+import { configureStore } from '@reduxjs/toolkit'
+
+import { combineReducers } from '@reduxjs/toolkit'
+import { useDispatch } from 'react-redux'
+import threads from './threads'
+
+const rootReducer = combineReducers({
+ threads
+})
+
+const store = configureStore({
+ reducer: rootReducer
+})
+
+export type RootState = ReturnType
+export type AppDispatch = typeof store.dispatch
+export const useAppDispatch = useDispatch.withTypes()
+
+export default store
diff --git a/src/renderer/src/store/threads.ts b/src/renderer/src/store/threads.ts
new file mode 100644
index 00000000..2a3003a3
--- /dev/null
+++ b/src/renderer/src/store/threads.ts
@@ -0,0 +1,30 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+import { Thread } from '@renderer/types'
+
+interface State {
+ threads: Thread[]
+}
+
+const initialState: State = {
+ threads: []
+}
+
+const threadsSlice = createSlice({
+ name: 'threads',
+ initialState,
+ reducers: {
+ addThread: (state, action: PayloadAction) => {
+ state.threads.push(action.payload)
+ },
+ removeThread: (state, action: PayloadAction<{ id: string }>) => {
+ state.threads = state.threads.filter((c) => c.id !== action.payload.id)
+ },
+ updateThread: (state, action: PayloadAction) => {
+ state.threads = state.threads.map((c) => (c.id === action.payload.id ? action.payload : c))
+ }
+ }
+})
+
+export const { addThread, removeThread, updateThread } = threadsSlice.actions
+
+export default threadsSlice
diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts
new file mode 100644
index 00000000..c3f6021e
--- /dev/null
+++ b/src/renderer/src/types/index.ts
@@ -0,0 +1,7 @@
+export type Thread = {
+ id: string
+ name: string
+ avatar: string
+ lastMessage: string
+ lastMessageAt: string
+}
diff --git a/yarn.lock b/yarn.lock
index 6f966c0b..d2225dd0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -854,6 +854,16 @@
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
+"@reduxjs/toolkit@^2.2.5":
+ version "2.2.5"
+ resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.2.5.tgz#c0d2d8482ef80722bebe015ff05b06c34bfb6e0d"
+ integrity sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==
+ dependencies:
+ immer "^10.0.3"
+ redux "^5.0.1"
+ redux-thunk "^3.1.0"
+ reselect "^5.1.0"
+
"@remix-run/router@1.16.1":
version "1.16.1"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.16.1.tgz#73db3c48b975eeb06d0006481bde4f5f2d17d1cd"
@@ -1104,6 +1114,11 @@
resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.5.tgz#1daa6456f40959d06157698a653a9ab0a70281df"
integrity sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==
+"@types/use-sync-external-store@^0.0.3":
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
+ integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
+
"@types/verror@^1.10.3":
version "1.10.10"
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087"
@@ -2894,6 +2909,11 @@ immediate@~3.0.5:
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
+immer@^10.0.3:
+ version "10.1.1"
+ resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc"
+ integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==
+
immutable@^4.0.0:
version "4.3.6"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.6.tgz#6a05f7858213238e587fb83586ffa3b4b27f0447"
@@ -3780,6 +3800,14 @@ react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+react-redux@^9.1.2:
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.2.tgz#deba38c64c3403e9abd0c3fbeab69ffd9d8a7e4b"
+ integrity sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==
+ dependencies:
+ "@types/use-sync-external-store" "^0.0.3"
+ use-sync-external-store "^1.0.0"
+
react-refresh@^0.14.0:
version "0.14.2"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
@@ -3826,6 +3854,21 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+redux-persist@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
+ integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==
+
+redux-thunk@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
+ integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==
+
+redux@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b"
+ integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==
+
reflect.getprototypeof@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859"
@@ -3859,6 +3902,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+reselect@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.0.tgz#c479139ab9dd91be4d9c764a7f3868210ef8cd21"
+ integrity sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg==
+
resolve-alpn@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
@@ -4461,6 +4509,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+use-sync-external-store@^1.0.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
+ integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
+
utf8-byte-length@^1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e"