feat: add home page

This commit is contained in:
kangfenmao
2024-05-24 12:38:51 +08:00
parent 6acd758342
commit e7ebc5022b
17 changed files with 492 additions and 224 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
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 icon from '@/renderer/resources/icon.png?asset'
import windowStateKeeper from 'electron-window-state'
function createWindow(): void {
@@ -19,6 +19,7 @@ function createWindow(): void {
height: mainWindowState.height,
show: false,
autoHideMenuBar: true,
titleBarStyle: 'hiddenInset',
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
+22 -28
View File
@@ -1,35 +1,29 @@
import Versions from './components/Versions'
import electronLogo from './assets/electron.svg'
import styled from 'styled-components'
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'
function App(): JSX.Element {
const ipcHandle = (): void => window.electron.ipcRenderer.send('ping')
return (
<>
<img alt="logo" className="logo" src={electronLogo} />
<div className="creator">Powered by electron-vite</div>
<div className="text">
Build an Electron app with <span className="react">React</span>
&nbsp;and <span className="ts">TypeScript</span>
</div>
<p className="tip">
Please try pressing <code>F12</code> to open the devTool
</p>
<div className="actions">
<div className="action">
<a href="https://electron-vite.org/" target="_blank" rel="noreferrer">
Documentation
</a>
</div>
<div className="action">
<a target="_blank" rel="noreferrer" onClick={ipcHandle}>
Send IPC
</a>
</div>
</div>
<Versions></Versions>
</>
<BrowserRouter>
<MainContainer>
<Sidebar />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/apps" element={<AppsPage />} />
</Routes>
<Statusbar />
</MainContainer>
</BrowserRouter>
)
}
const MainContainer = styled.main`
display: flex;
flex-direction: row;
flex: 1;
`
export default App
@@ -21,6 +21,12 @@
--ev-button-alt-hover-border: transparent;
--ev-button-alt-hover-text: var(--ev-c-text-1);
--ev-button-alt-hover-bg: var(--ev-c-gray-2);
--navbar-height: 48px;
--sidebar-width: 64px;
--conversations-width: 240px;
--settings-width: 320px;
--status-bar-height: 48px;
}
:root {
@@ -29,6 +35,7 @@
--color-background-mute: var(--ev-c-black-mute);
--color-text: var(--ev-c-text-1);
--color-text-2: var(--ev-c-text-2);
}
*,
@@ -65,3 +72,33 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
display: flex;
overflow: hidden;
background-size: cover;
user-select: none;
background-color: #0b0a09;
}
code {
font-weight: 600;
padding: 3px 5px;
border-radius: 2px;
background-color: var(--color-background-mute);
font-family:
ui-monospace,
SFMono-Regular,
SF Mono,
Menlo,
Consolas,
Liberation Mono,
monospace;
font-size: 85%;
}
#root {
display: flex;
flex-direction: column;
width: 100%;
}

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

-171
View File
@@ -1,171 +0,0 @@
@import './base.css';
body {
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background-image: url('./wavy-lines.svg');
background-size: cover;
user-select: none;
}
code {
font-weight: 600;
padding: 3px 5px;
border-radius: 2px;
background-color: var(--color-background-mute);
font-family:
ui-monospace,
SFMono-Regular,
SF Mono,
Menlo,
Consolas,
Liberation Mono,
monospace;
font-size: 85%;
}
#root {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-bottom: 80px;
}
.logo {
margin-bottom: 20px;
-webkit-user-drag: none;
height: 128px;
width: 128px;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 1.2em #6988e6aa);
}
.creator {
font-size: 14px;
line-height: 16px;
color: var(--ev-c-text-2);
font-weight: 600;
margin-bottom: 10px;
}
.text {
font-size: 28px;
color: var(--ev-c-text-1);
font-weight: 700;
line-height: 32px;
text-align: center;
margin: 0 10px;
padding: 16px 0;
}
.tip {
font-size: 16px;
line-height: 24px;
color: var(--ev-c-text-2);
font-weight: 600;
}
.react {
background: -webkit-linear-gradient(315deg, #087ea4 55%, #7c93ee);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 700;
}
.ts {
background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 700;
}
.actions {
display: flex;
padding-top: 32px;
margin: -6px;
flex-wrap: wrap;
justify-content: flex-start;
}
.action {
flex-shrink: 0;
padding: 6px;
}
.action a {
cursor: pointer;
text-decoration: none;
display: inline-block;
border: 1px solid transparent;
text-align: center;
font-weight: 600;
white-space: nowrap;
border-radius: 20px;
padding: 0 20px;
line-height: 38px;
font-size: 14px;
border-color: var(--ev-button-alt-border);
color: var(--ev-button-alt-text);
background-color: var(--ev-button-alt-bg);
}
.action a:hover {
border-color: var(--ev-button-alt-hover-border);
color: var(--ev-button-alt-hover-text);
background-color: var(--ev-button-alt-hover-bg);
}
.versions {
position: absolute;
bottom: 30px;
margin: 0 auto;
padding: 15px 0;
font-family: 'Menlo', 'Lucida Console', monospace;
display: inline-flex;
overflow: hidden;
align-items: center;
border-radius: 22px;
background-color: #202127;
backdrop-filter: blur(24px);
}
.versions li {
display: block;
float: left;
border-right: 1px solid var(--ev-c-gray-1);
padding: 0 20px;
font-size: 14px;
line-height: 14px;
opacity: 0.8;
&:last-child {
border: none;
}
}
@media (max-width: 720px) {
.text {
font-size: 20px;
}
}
@media (max-width: 620px) {
.versions {
display: none;
}
}
@media (max-width: 350px) {
.tip,
.actions {
display: none;
}
}
-15
View File
@@ -1,15 +0,0 @@
import { useState } from 'react'
function Versions(): JSX.Element {
const [versions] = useState(window.electron.process.versions)
return (
<ul className="versions">
<li className="electron-version">Electron v{versions.electron}</li>
<li className="chrome-version">Chromium v{versions.chrome}</li>
<li className="node-version">Node v{versions.node}</li>
</ul>
)
}
export default Versions
@@ -0,0 +1,50 @@
import { FC, PropsWithChildren } from 'react'
import styled from 'styled-components'
interface Props extends PropsWithChildren {}
export const Navbar: FC<Props> = ({ children }) => {
return <NavbarContainer>{children}</NavbarContainer>
}
export const NavbarLeft: FC<Props> = ({ children }) => {
return <NavbarLeftContainer>{children}</NavbarLeftContainer>
}
export const NavbarCenter: FC<Props> = ({ children }) => {
return <NavbarCenterContainer>{children}</NavbarCenterContainer>
}
export const NavbarRight: FC<Props> = ({ children }) => {
return <NavbarRightContainer>{children}</NavbarRightContainer>
}
const NavbarContainer = styled.div`
min-width: 100%;
display: flex;
flex-direction: row;
height: var(--navbar-height);
border-bottom: 1px solid #ffffff20;
-webkit-app-region: drag;
`
const NavbarLeftContainer = styled.div`
min-width: var(--conversations-width);
border-right: 1px solid #ffffff20;
`
const NavbarCenterContainer = styled.div`
flex: 1;
display: flex;
align-items: center;
font-size: 14px;
font-weight: bold;
color: var(--color-text-1);
text-align: center;
border-right: 1px solid #ffffff20;
padding: 0 16px;
`
const NavbarRightContainer = styled.div`
min-width: var(--settings-width);
`
@@ -0,0 +1,91 @@
import { FC } from 'react'
import Logo from '@renderer/assets/images/electron.svg'
import AppIcon from '@renderer/assets/images/sidebar_app_active.png'
import MessageIcon from '@renderer/assets/images/sidebar_message_active.png'
import styled from 'styled-components'
import { Link, useLocation } from 'react-router-dom'
const Sidebar: FC = () => {
const { pathname } = useLocation()
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
return (
<Container>
<Avatar>
<AvatarImg src={Logo} />
</Avatar>
<Menus>
<Link to="/">
<Icon className={isRoute('/')}>
<IconImage src={MessageIcon} />
</Icon>
</Link>
<Link to="/apps">
<Icon className={isRoute('/apps')}>
<IconImage src={AppIcon} />
</Icon>
</Link>
</Menus>
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 0;
min-width: var(--sidebar-width);
min-height: 100%;
border-top: 1px solid #ffffff20;
border-right: 1px solid #ffffff20;
margin-top: 47px;
`
const Avatar = styled.div``
const AvatarImg = styled.img`
border-radius: 50%;
width: 36px;
height: 36px;
background-color: var(--color-background-soft);
margin: 5px 0;
`
const Menus = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`
const Icon = styled.div`
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
margin-bottom: 5px;
transition: background-color 0.2s ease;
&:hover {
background-color: #ffffff30;
cursor: pointer;
.icon-img {
filter: invert(1);
}
}
&.active {
background-color: #ffffff20;
.icon-img {
filter: invert(1);
}
}
`
const IconImage = styled.img`
width: 20px;
height: 20px;
filter: invert(0.6);
transition: filter 0.2s ease;
`
export default Sidebar
@@ -0,0 +1,46 @@
import { FC } from 'react'
import styled from 'styled-components'
const Statusbar: FC = () => {
return (
<Container>
<StatusbarLeft />
<StatusbarCenter />
<StatusbarRight>Cherry AI v0.1.0</StatusbarRight>
</Container>
)
}
const Container = styled.div`
min-height: var(--status-bar-height);
border-top: 1px solid #ffffff20;
display: flex;
flex-direction: row;
position: absolute;
bottom: 0;
left: var(--sidebar-width);
right: 0;
background-color: #0b0a09;
`
const StatusbarLeft = styled.div`
min-width: var(--sidebar-width) + var(--conversations-width);
`
const StatusbarCenter = styled.div`
flex: 1;
display: flex;
`
const StatusbarRight = styled.div`
min-width: var(--settings-width);
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
font-size: 12px;
color: var(--color-text-2);
padding-right: 16px;
`
export default Statusbar
+1 -1
View File
@@ -1,4 +1,4 @@
import './assets/main.css'
import './assets/css/base.css'
import React from 'react'
import ReactDOM from 'react-dom/client'
+20
View File
@@ -0,0 +1,20 @@
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import { FC } from 'react'
import styled from 'styled-components'
const AppsPage: FC = () => {
return (
<Container>
<Navbar>
<NavbarCenter>APP</NavbarCenter>
</Navbar>
</Container>
)
}
const Container = styled.div`
display: flex;
flex: 1;
`
export default AppsPage
+54
View File
@@ -0,0 +1,54 @@
import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
import { FC } from 'react'
import styled from 'styled-components'
const HomePage: FC = () => {
return (
<MainContainer>
<Navbar>
<NavbarLeft />
<NavbarCenter>Cherry AI</NavbarCenter>
<NavbarRight />
</Navbar>
<ContentContainer>
<Conversations />
<Chat />
<Settings />
</ContentContainer>
</MainContainer>
)
}
const MainContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
`
const ContentContainer = styled.div`
display: flex;
flex: 1;
flex-direction: row;
`
const Conversations = styled.div`
display: flex;
min-width: var(--conversations-width);
border-right: 1px solid #ffffff20;
height: 100%;
`
const Chat = styled.div`
display: flex;
height: 100%;
flex: 1;
border-right: 1px solid #ffffff20;
`
const Settings = styled.div`
display: flex;
height: 100%;
min-width: var(--settings-width);
`
export default HomePage