43 |
--------------------------------------------------------------------------------
/src/preload/index.d.ts:
--------------------------------------------------------------------------------
1 | import { ElectronAPI } from '@electron-toolkit/preload'
2 |
3 | declare global {
4 | interface Window {
5 | electron: ElectronAPI
6 | api: unknown
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { contextBridge } from 'electron'
2 | import { electronAPI } from '@electron-toolkit/preload'
3 |
4 | // Custom APIs for renderer
5 | const api = {}
6 |
7 | // Use `contextBridge` APIs to expose Electron APIs to
8 | // renderer only if context isolation is enabled, otherwise
9 | // just add to the DOM global.
10 | if (process.contextIsolated) {
11 | try {
12 | contextBridge.exposeInMainWorld('electron', electronAPI)
13 | contextBridge.exposeInMainWorld('api', api)
14 | } catch (error) {
15 | console.error(error)
16 | }
17 | } else {
18 | // @ts-ignore (define in dts)
19 | window.electron = electronAPI
20 | // @ts-ignore (define in dts)
21 | window.api = api
22 | }
23 |
--------------------------------------------------------------------------------
/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/renderer/src/App.tsx:
--------------------------------------------------------------------------------
1 | import electronLogo from './assets/electron.svg'
2 | import Versions from './components/Versions'
3 | import { ipc } from './ipc'
4 |
5 | function App(): JSX.Element {
6 | const actions = [
7 | {
8 | label: '最小化',
9 | handler: () => {
10 | ipc.hideWindow()
11 | }
12 | },
13 | {
14 | label: '获取窗口尺寸',
15 | handler: async () => {
16 | const sizes = await ipc.getWindowSize()
17 | alert(`窗口尺寸:${sizes?.join(' x ')}`)
18 | }
19 | },
20 | {
21 | label: '调用异步的 ipc 方法',
22 | handler: async () => {
23 | const res = await ipc.asyncDemo()
24 | alert(res)
25 | }
26 | },
27 | {
28 | label: '传参到主进程(查看主进程console)',
29 | handler: async () => {
30 | await ipc.paramsDemo('来自渲染进程的问候', '---- 爱你的老鼠哥')
31 | }
32 | }
33 | ]
34 |
35 | return (
36 | <>
37 |
38 | 基于 Proxy 实现的 ipc 演示
39 |
40 | 绝佳的 类型体验
41 | 和极低 心智负担
42 |
43 |
44 | 以下按钮事件从渲染进程调用主进程服务,请拉取并修改 click 中的代码以体验 ts 的魔力
45 |
46 |
47 | {actions.map((it) => {
48 | return (
49 |
54 | )
55 | })}
56 |
57 |
64 | 基于以下黑魔法
65 |
66 | >
67 | )
68 | }
69 |
70 | export default App
71 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/base.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --ev-c-white: #ffffff;
3 | --ev-c-white-soft: #f8f8f8;
4 | --ev-c-white-mute: #f2f2f2;
5 |
6 | --ev-c-black: #1b1b1f;
7 | --ev-c-black-soft: #222222;
8 | --ev-c-black-mute: #282828;
9 |
10 | --ev-c-gray-1: #515c67;
11 | --ev-c-gray-2: #414853;
12 | --ev-c-gray-3: #32363f;
13 |
14 | --ev-c-text-1: rgba(255, 255, 245, 0.86);
15 | --ev-c-text-2: rgba(235, 235, 245, 0.6);
16 | --ev-c-text-3: rgba(235, 235, 245, 0.38);
17 |
18 | --ev-button-alt-border: transparent;
19 | --ev-button-alt-text: var(--ev-c-text-1);
20 | --ev-button-alt-bg: var(--ev-c-gray-3);
21 | --ev-button-alt-hover-border: transparent;
22 | --ev-button-alt-hover-text: var(--ev-c-text-1);
23 | --ev-button-alt-hover-bg: var(--ev-c-gray-2);
24 | }
25 |
26 | :root {
27 | --color-background: var(--ev-c-black);
28 | --color-background-soft: var(--ev-c-black-soft);
29 | --color-background-mute: var(--ev-c-black-mute);
30 |
31 | --color-text: var(--ev-c-text-1);
32 | }
33 |
34 | *,
35 | *::before,
36 | *::after {
37 | box-sizing: border-box;
38 | margin: 0;
39 | font-weight: normal;
40 | }
41 |
42 | ul {
43 | list-style: none;
44 | }
45 |
46 | body {
47 | min-height: 100vh;
48 | color: var(--color-text);
49 | background: var(--color-background);
50 | line-height: 1.6;
51 | font-family:
52 | Inter,
53 | -apple-system,
54 | BlinkMacSystemFont,
55 | 'Segoe UI',
56 | Roboto,
57 | Oxygen,
58 | Ubuntu,
59 | Cantarell,
60 | 'Fira Sans',
61 | 'Droid Sans',
62 | 'Helvetica Neue',
63 | sans-serif;
64 | text-rendering: optimizeLegibility;
65 | -webkit-font-smoothing: antialiased;
66 | -moz-osx-font-smoothing: grayscale;
67 | }
68 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/electron.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/main.css:
--------------------------------------------------------------------------------
1 | @import './base.css';
2 |
3 | body {
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | overflow: hidden;
8 | background-image: url('./wavy-lines.svg');
9 | background-size: cover;
10 | user-select: none;
11 | }
12 |
13 | code {
14 | font-weight: 600;
15 | padding: 3px 5px;
16 | border-radius: 2px;
17 | background-color: var(--color-background-mute);
18 | font-family:
19 | ui-monospace,
20 | SFMono-Regular,
21 | SF Mono,
22 | Menlo,
23 | Consolas,
24 | Liberation Mono,
25 | monospace;
26 | font-size: 85%;
27 | }
28 |
29 | #root {
30 | display: flex;
31 | align-items: center;
32 | justify-content: center;
33 | flex-direction: column;
34 | margin-bottom: 80px;
35 | }
36 |
37 | .logo {
38 | margin-bottom: 20px;
39 | -webkit-user-drag: none;
40 | height: 128px;
41 | width: 128px;
42 | will-change: filter;
43 | transition: filter 300ms;
44 | }
45 |
46 | .logo:hover {
47 | filter: drop-shadow(0 0 1.2em #6988e6aa);
48 | }
49 |
50 | .creator {
51 | font-size: 14px;
52 | line-height: 16px;
53 | color: var(--ev-c-text-2);
54 | font-weight: 600;
55 | margin-bottom: 10px;
56 | }
57 |
58 | .text {
59 | font-size: 28px;
60 | color: var(--ev-c-text-1);
61 | font-weight: 700;
62 | line-height: 32px;
63 | text-align: center;
64 | margin: 0 10px;
65 | padding: 16px 0;
66 | }
67 |
68 | .tip {
69 | font-size: 16px;
70 | line-height: 24px;
71 | color: var(--ev-c-text-2);
72 | font-weight: 600;
73 | }
74 |
75 | .react {
76 | background: -webkit-linear-gradient(315deg, #087ea4 55%, #7c93ee);
77 | background-clip: text;
78 | -webkit-background-clip: text;
79 | -webkit-text-fill-color: transparent;
80 | font-weight: 700;
81 | }
82 |
83 | .ts {
84 | background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e);
85 | background-clip: text;
86 | -webkit-background-clip: text;
87 | -webkit-text-fill-color: transparent;
88 | font-weight: 700;
89 | }
90 |
91 | .actions {
92 | display: flex;
93 | padding-top: 32px;
94 | margin: -6px;
95 | flex-wrap: wrap;
96 | justify-content: flex-start;
97 | }
98 |
99 | .action {
100 | flex-shrink: 0;
101 | padding: 6px;
102 | }
103 |
104 | .action a {
105 | cursor: pointer;
106 | text-decoration: none;
107 | display: inline-block;
108 | border: 1px solid transparent;
109 | text-align: center;
110 | font-weight: 600;
111 | white-space: nowrap;
112 | border-radius: 20px;
113 | padding: 0 20px;
114 | line-height: 38px;
115 | font-size: 14px;
116 | border-color: var(--ev-button-alt-border);
117 | color: var(--ev-button-alt-text);
118 | background-color: var(--ev-button-alt-bg);
119 | }
120 |
121 | .action a:hover {
122 | border-color: var(--ev-button-alt-hover-border);
123 | color: var(--ev-button-alt-hover-text);
124 | background-color: var(--ev-button-alt-hover-bg);
125 | }
126 |
127 | .versions {
128 | position: absolute;
129 | bottom: 30px;
130 | margin: 0 auto;
131 | padding: 15px 0;
132 | font-family: 'Menlo', 'Lucida Console', monospace;
133 | display: inline-flex;
134 | overflow: hidden;
135 | align-items: center;
136 | border-radius: 22px;
137 | background-color: #202127;
138 | backdrop-filter: blur(24px);
139 | }
140 |
141 | .versions li {
142 | display: block;
143 | float: left;
144 | border-right: 1px solid var(--ev-c-gray-1);
145 | padding: 0 20px;
146 | font-size: 14px;
147 | line-height: 14px;
148 | opacity: 0.8;
149 | &:last-child {
150 | border: none;
151 | }
152 | }
153 |
154 | @media (max-width: 720px) {
155 | .text {
156 | font-size: 20px;
157 | }
158 | }
159 |
160 | @media (max-width: 620px) {
161 | .versions {
162 | display: none;
163 | }
164 | }
165 |
166 | @media (max-width: 350px) {
167 | .tip,
168 | .actions {
169 | display: none;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/wavy-lines.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Versions.tsx:
--------------------------------------------------------------------------------
1 |
2 | function Versions(): JSX.Element {
3 | return (
4 |
5 | - cls-hooked
6 | - Proxy
7 | - ClassAsyncify
8 |
9 | )
10 | }
11 |
12 | export default Versions
13 |
--------------------------------------------------------------------------------
/src/renderer/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/renderer/src/ipc.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | export const ipc: import('../../main/ipc').Ipc = new Proxy(
3 | {},
4 | {
5 | get(_, action: string) {
6 | return async (...args: any[]) => {
7 | return await window.electron.ipcRenderer.invoke('ipc', {
8 | action,
9 | args
10 | })
11 | }
12 | }
13 | }
14 | ) as any
15 |
--------------------------------------------------------------------------------
/src/renderer/src/main.tsx:
--------------------------------------------------------------------------------
1 | import './assets/main.css'
2 |
3 | import React from 'react'
4 | import ReactDOM from 'react-dom/client'
5 | import App from './App'
6 |
7 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
8 |
9 |
10 |
11 | )
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
3 | "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "types": ["electron-vite/node"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
3 | "include": [
4 | "src/renderer/src/env.d.ts",
5 | "src/renderer/src/**/*",
6 | "src/renderer/src/**/*.tsx",
7 | "src/preload/*.d.ts"
8 | ],
9 | "compilerOptions": {
10 | "composite": true,
11 | "jsx": "react-jsx",
12 | "baseUrl": ".",
13 | "paths": {
14 | "@renderer/*": [
15 | "src/renderer/src/*"
16 | ]
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------