feat: add home page
This commit is contained in:
+2
-1
@@ -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
@@ -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>
|
||||
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 |
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,4 +1,4 @@
|
||||
import './assets/main.css'
|
||||
import './assets/css/base.css'
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user