├── .editorconfig
├── .eslintignore
├── .eslintrc.cjs
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.yaml
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── README.md
├── build
├── entitlements.mac.plist
├── icon.icns
├── icon.ico
└── icon.png
├── components.json
├── dev-app-update.yml
├── electron-builder.yml
├── electron.vite.config.ts
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── resources
└── icon.png
├── src
├── main
│ └── index.ts
├── preload
│ ├── index.d.ts
│ └── index.ts
└── renderer
│ ├── index.html
│ └── src
│ ├── App.tsx
│ ├── assets
│ ├── base.css
│ ├── electron.svg
│ ├── main.css
│ └── wavy-lines.svg
│ ├── components
│ ├── Versions.tsx
│ └── ui
│ │ ├── alert-dialog.tsx
│ │ └── button.tsx
│ ├── env.d.ts
│ ├── lib
│ └── utils.ts
│ └── main.tsx
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
└── tsconfig.web.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | out
4 | .gitignore
5 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'eslint:recommended',
4 | 'plugin:react/recommended',
5 | 'plugin:react/jsx-runtime',
6 | '@electron-toolkit/eslint-config-ts/recommended',
7 | '@electron-toolkit/eslint-config-prettier'
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | out
4 | .DS_Store
5 | *.log*
6 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | electron_mirror=https://npmmirror.com/mirrors/electron/
2 | electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
3 | shamefully-hoist=true
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | out
2 | dist
3 | pnpm-lock.yaml
4 | LICENSE.md
5 | tsconfig.json
6 | tsconfig.*.json
7 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | semi: false
3 | printWidth: 100
4 | trailingComma: none
5 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["dbaeumer.vscode-eslint"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug Main Process",
6 | "type": "node",
7 | "request": "launch",
8 | "cwd": "${workspaceRoot}",
9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
10 | "windows": {
11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
12 | },
13 | "runtimeArgs": ["--sourcemap"],
14 | "env": {
15 | "REMOTE_DEBUGGING_PORT": "9222"
16 | }
17 | },
18 | {
19 | "name": "Debug Renderer Process",
20 | "port": 9222,
21 | "request": "attach",
22 | "type": "chrome",
23 | "webRoot": "${workspaceFolder}/src/renderer",
24 | "timeout": 60000,
25 | "presentation": {
26 | "hidden": true
27 | }
28 | }
29 | ],
30 | "compounds": [
31 | {
32 | "name": "Debug All",
33 | "configurations": ["Debug Main Process", "Debug Renderer Process"],
34 | "presentation": {
35 | "order": 1
36 | }
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[typescript]": {
3 | "editor.defaultFormatter": "esbenp.prettier-vscode"
4 | },
5 | "[javascript]": {
6 | "editor.defaultFormatter": "esbenp.prettier-vscode"
7 | },
8 | "[json]": {
9 | "editor.defaultFormatter": "esbenp.prettier-vscode"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # shadcn-electron-app
2 |
3 | ## Installation
4 |
5 | ### 1. Create project
6 |
7 | ```bash
8 | $ pnpm create @quick-start/electron
9 | ```
10 |
11 | ### 2. Install dependencies
12 |
13 | ```bash
14 | $ pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
15 | ```
16 |
17 | ### 3. Install Tailwind CSS
18 |
19 | ```bash
20 | $ pnpm add -D tailwindcss postcss autoprefixer
21 |
22 | $ pnpm dlx tailwindcss init -p
23 | ```
24 |
25 | ### 4. Update `tailwind.config.js`
26 |
27 | ```js
28 | /** @type {import('tailwindcss').Config} */
29 | module.exports = {
30 | content: ['./src/renderer/src/**/*.{js,ts,jsx,tsx}'],
31 | theme: {
32 | container: {
33 | center: true,
34 | padding: '2rem',
35 | screens: {
36 | '2xl': '1400px'
37 | }
38 | },
39 | extend: {
40 | colors: {
41 | border: 'hsl(var(--border))',
42 | input: 'hsl(var(--input))',
43 | ring: 'hsl(var(--ring))',
44 | background: 'hsl(var(--background))',
45 | foreground: 'hsl(var(--foreground))',
46 | primary: {
47 | DEFAULT: 'hsl(var(--primary))',
48 | foreground: 'hsl(var(--primary-foreground))'
49 | },
50 | secondary: {
51 | DEFAULT: 'hsl(var(--secondary))',
52 | foreground: 'hsl(var(--secondary-foreground))'
53 | },
54 | destructive: {
55 | DEFAULT: 'hsl(var(--destructive))',
56 | foreground: 'hsl(var(--destructive-foreground))'
57 | },
58 | muted: {
59 | DEFAULT: 'hsl(var(--muted))',
60 | foreground: 'hsl(var(--muted-foreground))'
61 | },
62 | accent: {
63 | DEFAULT: 'hsl(var(--accent))',
64 | foreground: 'hsl(var(--accent-foreground))'
65 | },
66 | popover: {
67 | DEFAULT: 'hsl(var(--popover))',
68 | foreground: 'hsl(var(--popover-foreground))'
69 | },
70 | card: {
71 | DEFAULT: 'hsl(var(--card))',
72 | foreground: 'hsl(var(--card-foreground))'
73 | }
74 | },
75 | borderRadius: {
76 | lg: `var(--radius)`,
77 | md: `calc(var(--radius) - 2px)`,
78 | sm: 'calc(var(--radius) - 4px)'
79 | },
80 | keyframes: {
81 | 'accordion-down': {
82 | from: { height: '0' },
83 | to: { height: 'var(--radix-accordion-content-height)' }
84 | },
85 | 'accordion-up': {
86 | from: { height: 'var(--radix-accordion-content-height)' },
87 | to: { height: '0' }
88 | }
89 | },
90 | animation: {
91 | 'accordion-down': 'accordion-down 0.2s ease-out',
92 | 'accordion-up': 'accordion-up 0.2s ease-out'
93 | }
94 | }
95 | },
96 | plugins: [require('tailwindcss-animate')]
97 | }
98 | ```
99 |
100 | ### 5. Update `tsconfig.json`
101 |
102 | ```json
103 | {
104 | "compilerOptions": {
105 | "baseUrl": ".",
106 | "paths": {
107 | "@/*": ["./src/renderer/src/*"]
108 | }
109 | }
110 | }
111 | ```
112 |
113 | ### 6. Update `tsconfig.web.json`
114 |
115 | ```json
116 | {
117 | "compilerOptions": {
118 | "composite": true,
119 | "jsx": "react-jsx",
120 | "baseUrl": ".",
121 | "paths": {
122 | "@/*": ["./src/renderer/src/*"],
123 | "@renderer/*": ["src/renderer/src/*"]
124 | }
125 | }
126 | }
127 | ```
128 |
129 | ### 7. Create `components.json`
130 |
131 | ```json
132 | {
133 | "style": "new-york",
134 | "tailwind": {
135 | "config": "tailwind.config.js",
136 | "css": "src/renderer/src/assets/base.css",
137 | "baseColor": "zinc",
138 | "cssVariables": true,
139 | "prefix": ""
140 | },
141 | "rsc": false,
142 | "aliases": {
143 | "utils": "@/lib/utils",
144 | "components": "@/components",
145 | "lib": "@/lib",
146 | "hooks": "@/lib/hooks",
147 | "ui": "@/components/ui"
148 | },
149 | "iconLibrary": "lucide"
150 | }
151 | ```
152 |
153 | ### 8. Update `src/renderer/src/assets/base.css`
154 |
155 | ```css
156 | @tailwind base;
157 | @tailwind components;
158 | @tailwind utilities;
159 |
160 | @layer base {
161 | :root {
162 | --background: 0 0% 100%;
163 | --foreground: 222.2 47.4% 11.2%;
164 | --muted: 210 40% 96.1%;
165 | --muted-foreground: 215.4 16.3% 46.9%;
166 | --popover: 0 0% 100%;
167 | --popover-foreground: 222.2 47.4% 11.2%;
168 | --border: 214.3 31.8% 91.4%;
169 | --input: 214.3 31.8% 91.4%;
170 | --card: 0 0% 100%;
171 | --card-foreground: 222.2 47.4% 11.2%;
172 | --primary: 222.2 47.4% 11.2%;
173 | --primary-foreground: 210 40% 98%;
174 | --secondary: 210 40% 96.1%;
175 | --secondary-foreground: 222.2 47.4% 11.2%;
176 | --accent: 210 40% 96.1%;
177 | --accent-foreground: 222.2 47.4% 11.2%;
178 | --destructive: 0 100% 50%;
179 | --destructive-foreground: 210 40% 98%;
180 | --ring: 215 20.2% 65.1%;
181 | --radius: 0.5rem;
182 | }
183 |
184 | .dark {
185 | --background: 224 71% 4%;
186 | --foreground: 213 31% 91%;
187 | --muted: 223 47% 11%;
188 | --muted-foreground: 215.4 16.3% 56.9%;
189 | --accent: 216 34% 17%;
190 | --accent-foreground: 210 40% 98%;
191 | --popover: 224 71% 4%;
192 | --popover-foreground: 215 20.2% 65.1%;
193 | --border: 216 34% 17%;
194 | --input: 216 34% 17%;
195 | --card: 224 71% 4%;
196 | --card-foreground: 213 31% 91%;
197 | --primary: 210 40% 98%;
198 | --primary-foreground: 222.2 47.4% 1.2%;
199 | --secondary: 222.2 47.4% 11.2%;
200 | --secondary-foreground: 210 40% 98%;
201 | --destructive: 0 63% 31%;
202 | --destructive-foreground: 210 40% 98%;
203 | --ring: 216 34% 17%;
204 | --radius: 0.5rem;
205 | }
206 | }
207 |
208 | @layer base {
209 | * {
210 | @apply border-border;
211 | }
212 | body {
213 | @apply font-sans antialiased bg-background text-foreground;
214 | }
215 | }
216 | ```
217 |
--------------------------------------------------------------------------------
/build/entitlements.mac.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.allow-jit
6 |
7 | com.apple.security.cs.allow-unsigned-executable-memory
8 |
9 | com.apple.security.cs.allow-dyld-environment-variables
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadcn/shadcn-electron-app/9b731838ab5891f95c8c80c01eb84695ed88d37d/build/icon.icns
--------------------------------------------------------------------------------
/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadcn/shadcn-electron-app/9b731838ab5891f95c8c80c01eb84695ed88d37d/build/icon.ico
--------------------------------------------------------------------------------
/build/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadcn/shadcn-electron-app/9b731838ab5891f95c8c80c01eb84695ed88d37d/build/icon.png
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "style": "new-york",
3 | "tailwind": {
4 | "config": "tailwind.config.js",
5 | "css": "src/renderer/src/assets/base.css",
6 | "baseColor": "zinc",
7 | "cssVariables": true,
8 | "prefix": ""
9 | },
10 | "rsc": false,
11 | "aliases": {
12 | "utils": "@/lib/utils",
13 | "components": "@/components",
14 | "lib": "@/lib",
15 | "hooks": "@/lib/hooks",
16 | "ui": "@/components/ui"
17 | },
18 | "iconLibrary": "lucide"
19 | }
20 |
--------------------------------------------------------------------------------
/dev-app-update.yml:
--------------------------------------------------------------------------------
1 | provider: generic
2 | url: https://example.com/auto-updates
3 | updaterCacheDirName: shadcn-electron-app-updater
4 |
--------------------------------------------------------------------------------
/electron-builder.yml:
--------------------------------------------------------------------------------
1 | appId: com.electron.app
2 | productName: shadcn-electron-app
3 | directories:
4 | buildResources: build
5 | files:
6 | - '!**/.vscode/*'
7 | - '!src/*'
8 | - '!electron.vite.config.{js,ts,mjs,cjs}'
9 | - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
10 | - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
11 | - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
12 | asarUnpack:
13 | - resources/**
14 | win:
15 | executableName: shadcn-electron-app
16 | nsis:
17 | artifactName: ${name}-${version}-setup.${ext}
18 | shortcutName: ${productName}
19 | uninstallDisplayName: ${productName}
20 | createDesktopShortcut: always
21 | mac:
22 | entitlementsInherit: build/entitlements.mac.plist
23 | extendInfo:
24 | - NSCameraUsageDescription: Application requests access to the device's camera.
25 | - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
26 | - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
27 | - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
28 | notarize: false
29 | dmg:
30 | artifactName: ${name}-${version}.${ext}
31 | linux:
32 | target:
33 | - AppImage
34 | - snap
35 | - deb
36 | maintainer: electronjs.org
37 | category: Utility
38 | appImage:
39 | artifactName: ${name}-${version}.${ext}
40 | npmRebuild: false
41 | publish:
42 | provider: generic
43 | url: https://example.com/auto-updates
44 | electronDownload:
45 | mirror: https://npmmirror.com/mirrors/electron/
46 |
--------------------------------------------------------------------------------
/electron.vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'path'
2 | import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
3 | import react from '@vitejs/plugin-react'
4 |
5 | export default defineConfig({
6 | main: {
7 | plugins: [externalizeDepsPlugin()]
8 | },
9 | preload: {
10 | plugins: [externalizeDepsPlugin()]
11 | },
12 | renderer: {
13 | resolve: {
14 | alias: {
15 | '@renderer': resolve('src/renderer/src'),
16 | '@': resolve('src/renderer/src')
17 | }
18 | },
19 | plugins: [react()]
20 | }
21 | })
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shadcn-electron-app",
3 | "version": "1.0.0",
4 | "description": "An Electron application with React and TypeScript",
5 | "main": "./out/main/index.js",
6 | "author": "example.com",
7 | "homepage": "https://electron-vite.org",
8 | "scripts": {
9 | "format": "prettier --write .",
10 | "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
11 | "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
12 | "typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
13 | "typecheck": "npm run typecheck:node && npm run typecheck:web",
14 | "start": "electron-vite preview",
15 | "dev": "electron-vite dev",
16 | "build": "npm run typecheck && electron-vite build",
17 | "postinstall": "electron-builder install-app-deps",
18 | "build:unpack": "npm run build && electron-builder --dir",
19 | "build:win": "npm run build && electron-builder --win",
20 | "build:mac": "electron-vite build && electron-builder --mac",
21 | "build:linux": "electron-vite build && electron-builder --linux"
22 | },
23 | "dependencies": {
24 | "@electron-toolkit/preload": "^3.0.1",
25 | "@electron-toolkit/utils": "^3.0.0",
26 | "@radix-ui/react-alert-dialog": "^1.1.2",
27 | "@radix-ui/react-slot": "^1.1.0",
28 | "class-variance-authority": "^0.7.0",
29 | "clsx": "^2.1.1",
30 | "electron-updater": "^6.1.7",
31 | "lucide-react": "^0.456.0",
32 | "tailwind-merge": "^2.5.4",
33 | "tailwindcss-animate": "^1.0.7"
34 | },
35 | "devDependencies": {
36 | "@electron-toolkit/eslint-config-prettier": "^2.0.0",
37 | "@electron-toolkit/eslint-config-ts": "^2.0.0",
38 | "@electron-toolkit/tsconfig": "^1.0.1",
39 | "@types/node": "^20.17.6",
40 | "@types/react": "^18.3.3",
41 | "@types/react-dom": "^18.3.0",
42 | "@vitejs/plugin-react": "^4.3.1",
43 | "autoprefixer": "^10.4.20",
44 | "electron": "^31.0.2",
45 | "electron-builder": "^24.13.3",
46 | "electron-vite": "^2.3.0",
47 | "eslint": "^8.57.0",
48 | "eslint-plugin-react": "^7.34.3",
49 | "postcss": "^8.4.49",
50 | "prettier": "^3.3.2",
51 | "react": "^18.3.1",
52 | "react-dom": "^18.3.1",
53 | "tailwindcss": "^3.4.14",
54 | "typescript": "^5.5.2",
55 | "vite": "^5.3.1"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadcn/shadcn-electron-app/9b731838ab5891f95c8c80c01eb84695ed88d37d/resources/icon.png
--------------------------------------------------------------------------------
/src/main/index.ts:
--------------------------------------------------------------------------------
1 | import { app, shell, BrowserWindow, ipcMain } from 'electron'
2 | import { join } from 'path'
3 | import { electronApp, optimizer, is } from '@electron-toolkit/utils'
4 | import icon from '../../resources/icon.png?asset'
5 |
6 | function createWindow(): void {
7 | // Create the browser window.
8 | const mainWindow = new BrowserWindow({
9 | width: 900,
10 | height: 670,
11 | show: false,
12 | autoHideMenuBar: true,
13 | ...(process.platform === 'linux' ? { icon } : {}),
14 | webPreferences: {
15 | preload: join(__dirname, '../preload/index.js'),
16 | sandbox: false
17 | }
18 | })
19 |
20 | mainWindow.on('ready-to-show', () => {
21 | mainWindow.show()
22 | })
23 |
24 | mainWindow.webContents.setWindowOpenHandler((details) => {
25 | shell.openExternal(details.url)
26 | return { action: 'deny' }
27 | })
28 |
29 | // HMR for renderer base on electron-vite cli.
30 | // Load the remote URL for development or the local html file for production.
31 | if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
32 | mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
33 | } else {
34 | mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
35 | }
36 | }
37 |
38 | // This method will be called when Electron has finished
39 | // initialization and is ready to create browser windows.
40 | // Some APIs can only be used after this event occurs.
41 | app.whenReady().then(() => {
42 | // Set app user model id for windows
43 | electronApp.setAppUserModelId('com.electron')
44 |
45 | // Default open or close DevTools by F12 in development
46 | // and ignore CommandOrControl + R in production.
47 | // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
48 | app.on('browser-window-created', (_, window) => {
49 | optimizer.watchWindowShortcuts(window)
50 | })
51 |
52 | // IPC test
53 | ipcMain.on('ping', () => console.log('pong'))
54 |
55 | createWindow()
56 |
57 | app.on('activate', function () {
58 | // On macOS it's common to re-create a window in the app when the
59 | // dock icon is clicked and there are no other windows open.
60 | if (BrowserWindow.getAllWindows().length === 0) createWindow()
61 | })
62 | })
63 |
64 | // Quit when all windows are closed, except on macOS. There, it's common
65 | // for applications and their menu bar to stay active until the user quits
66 | // explicitly with Cmd + Q.
67 | app.on('window-all-closed', () => {
68 | if (process.platform !== 'darwin') {
69 | app.quit()
70 | }
71 | })
72 |
73 | // In this file you can include the rest of your app"s specific main process
74 | // code. You can also put them in separate files and require them here.
75 |
--------------------------------------------------------------------------------
/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 {
2 | AlertDialog,
3 | AlertDialogAction,
4 | AlertDialogCancel,
5 | AlertDialogContent,
6 | AlertDialogDescription,
7 | AlertDialogFooter,
8 | AlertDialogHeader,
9 | AlertDialogTitle,
10 | AlertDialogTrigger
11 | } from '@/components/ui/alert-dialog'
12 | import { Button } from '@/components/ui/button'
13 |
14 | function App(): JSX.Element {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Are you absolutely sure?
24 |
25 | This action cannot be undone. This will permanently delete your account and remove
26 | your data from our servers.
27 |
28 |
29 |
30 | Cancel
31 | Continue
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | export default App
40 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/base.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 222.2 47.4% 11.2%;
9 | --muted: 210 40% 96.1%;
10 | --muted-foreground: 215.4 16.3% 46.9%;
11 | --popover: 0 0% 100%;
12 | --popover-foreground: 222.2 47.4% 11.2%;
13 | --border: 214.3 31.8% 91.4%;
14 | --input: 214.3 31.8% 91.4%;
15 | --card: 0 0% 100%;
16 | --card-foreground: 222.2 47.4% 11.2%;
17 | --primary: 222.2 47.4% 11.2%;
18 | --primary-foreground: 210 40% 98%;
19 | --secondary: 210 40% 96.1%;
20 | --secondary-foreground: 222.2 47.4% 11.2%;
21 | --accent: 210 40% 96.1%;
22 | --accent-foreground: 222.2 47.4% 11.2%;
23 | --destructive: 0 100% 50%;
24 | --destructive-foreground: 210 40% 98%;
25 | --ring: 215 20.2% 65.1%;
26 | --radius: 0.5rem;
27 | }
28 |
29 | .dark {
30 | --background: 224 71% 4%;
31 | --foreground: 213 31% 91%;
32 | --muted: 223 47% 11%;
33 | --muted-foreground: 215.4 16.3% 56.9%;
34 | --accent: 216 34% 17%;
35 | --accent-foreground: 210 40% 98%;
36 | --popover: 224 71% 4%;
37 | --popover-foreground: 215 20.2% 65.1%;
38 | --border: 216 34% 17%;
39 | --input: 216 34% 17%;
40 | --card: 224 71% 4%;
41 | --card-foreground: 213 31% 91%;
42 | --primary: 210 40% 98%;
43 | --primary-foreground: 222.2 47.4% 1.2%;
44 | --secondary: 222.2 47.4% 11.2%;
45 | --secondary-foreground: 210 40% 98%;
46 | --destructive: 0 63% 31%;
47 | --destructive-foreground: 210 40% 98%;
48 | --ring: 216 34% 17%;
49 | --radius: 0.5rem;
50 | }
51 | }
52 |
53 | @layer base {
54 | * {
55 | @apply border-border;
56 | }
57 | body {
58 | @apply font-sans antialiased bg-background text-foreground;
59 | font-feature-settings:
60 | 'rlig' 1,
61 | 'calt' 1;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/electron.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/main.css:
--------------------------------------------------------------------------------
1 | @import './base.css';
2 |
--------------------------------------------------------------------------------
/src/renderer/src/assets/wavy-lines.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Versions.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 |
3 | function Versions(): JSX.Element {
4 | const [versions] = useState(window.electron.process.versions)
5 |
6 | return (
7 |
8 | - Electronsds v{versions.electron}
9 | - Chromium v{versions.chrome}
10 | - Node v{versions.node}
11 |
12 | )
13 | }
14 |
15 | export default Versions
16 |
--------------------------------------------------------------------------------
/src/renderer/src/components/ui/alert-dialog.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
3 |
4 | import { cn } from '@/lib/utils'
5 | import { buttonVariants } from '@/components/ui/button'
6 |
7 | const AlertDialog = AlertDialogPrimitive.Root
8 |
9 | const AlertDialogTrigger = AlertDialogPrimitive.Trigger
10 |
11 | const AlertDialogPortal = AlertDialogPrimitive.Portal
12 |
13 | const AlertDialogOverlay = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef
16 | >(({ className, ...props }, ref) => (
17 |
25 | ))
26 | AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
27 |
28 | const AlertDialogContent = React.forwardRef<
29 | React.ElementRef,
30 | React.ComponentPropsWithoutRef
31 | >(({ className, ...props }, ref) => (
32 |
33 |
34 |
42 |
43 | ))
44 | AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
45 |
46 | const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
47 |
48 | )
49 | AlertDialogHeader.displayName = 'AlertDialogHeader'
50 |
51 | const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
52 |
56 | )
57 | AlertDialogFooter.displayName = 'AlertDialogFooter'
58 |
59 | const AlertDialogTitle = React.forwardRef<
60 | React.ElementRef,
61 | React.ComponentPropsWithoutRef
62 | >(({ className, ...props }, ref) => (
63 |
68 | ))
69 | AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
70 |
71 | const AlertDialogDescription = React.forwardRef<
72 | React.ElementRef,
73 | React.ComponentPropsWithoutRef
74 | >(({ className, ...props }, ref) => (
75 |
80 | ))
81 | AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName
82 |
83 | const AlertDialogAction = React.forwardRef<
84 | React.ElementRef,
85 | React.ComponentPropsWithoutRef
86 | >(({ className, ...props }, ref) => (
87 |
88 | ))
89 | AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
90 |
91 | const AlertDialogCancel = React.forwardRef<
92 | React.ElementRef,
93 | React.ComponentPropsWithoutRef
94 | >(({ className, ...props }, ref) => (
95 |
100 | ))
101 | AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
102 |
103 | export {
104 | AlertDialog,
105 | AlertDialogPortal,
106 | AlertDialogOverlay,
107 | AlertDialogTrigger,
108 | AlertDialogContent,
109 | AlertDialogHeader,
110 | AlertDialogFooter,
111 | AlertDialogTitle,
112 | AlertDialogDescription,
113 | AlertDialogAction,
114 | AlertDialogCancel
115 | }
116 |
--------------------------------------------------------------------------------
/src/renderer/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Slot } from '@radix-ui/react-slot'
3 | import { cva, type VariantProps } from 'class-variance-authority'
4 |
5 | import { cn } from '@/lib/utils'
6 |
7 | const buttonVariants = cva(
8 | 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
9 | {
10 | variants: {
11 | variant: {
12 | default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
13 | destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
14 | outline:
15 | 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
16 | secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
17 | ghost: 'hover:bg-accent hover:text-accent-foreground',
18 | link: 'text-primary underline-offset-4 hover:underline'
19 | },
20 | size: {
21 | default: 'h-9 px-4 py-2',
22 | sm: 'h-8 rounded-md px-3 text-xs',
23 | lg: 'h-10 rounded-md px-8',
24 | icon: 'h-9 w-9'
25 | }
26 | },
27 | defaultVariants: {
28 | variant: 'default',
29 | size: 'default'
30 | }
31 | }
32 | )
33 |
34 | export interface ButtonProps
35 | extends React.ButtonHTMLAttributes,
36 | VariantProps {
37 | asChild?: boolean
38 | }
39 |
40 | const Button = React.forwardRef(
41 | ({ className, variant, size, asChild = false, ...props }, ref) => {
42 | const Comp = asChild ? Slot : 'button'
43 | return (
44 |
45 | )
46 | }
47 | )
48 | Button.displayName = 'Button'
49 |
50 | export { Button, buttonVariants }
51 |
--------------------------------------------------------------------------------
/src/renderer/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/renderer/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from 'clsx'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | export function cn(...inputs: ClassValue[]): string {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./src/renderer/src/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | container: {
6 | center: true,
7 | padding: '2rem',
8 | screens: {
9 | '2xl': '1400px'
10 | }
11 | },
12 | extend: {
13 | colors: {
14 | border: 'hsl(var(--border))',
15 | input: 'hsl(var(--input))',
16 | ring: 'hsl(var(--ring))',
17 | background: 'hsl(var(--background))',
18 | foreground: 'hsl(var(--foreground))',
19 | primary: {
20 | DEFAULT: 'hsl(var(--primary))',
21 | foreground: 'hsl(var(--primary-foreground))'
22 | },
23 | secondary: {
24 | DEFAULT: 'hsl(var(--secondary))',
25 | foreground: 'hsl(var(--secondary-foreground))'
26 | },
27 | destructive: {
28 | DEFAULT: 'hsl(var(--destructive))',
29 | foreground: 'hsl(var(--destructive-foreground))'
30 | },
31 | muted: {
32 | DEFAULT: 'hsl(var(--muted))',
33 | foreground: 'hsl(var(--muted-foreground))'
34 | },
35 | accent: {
36 | DEFAULT: 'hsl(var(--accent))',
37 | foreground: 'hsl(var(--accent-foreground))'
38 | },
39 | popover: {
40 | DEFAULT: 'hsl(var(--popover))',
41 | foreground: 'hsl(var(--popover-foreground))'
42 | },
43 | card: {
44 | DEFAULT: 'hsl(var(--card))',
45 | foreground: 'hsl(var(--card-foreground))'
46 | }
47 | },
48 | borderRadius: {
49 | lg: `var(--radius)`,
50 | md: `calc(var(--radius) - 2px)`,
51 | sm: 'calc(var(--radius) - 4px)'
52 | },
53 | keyframes: {
54 | 'accordion-down': {
55 | from: { height: '0' },
56 | to: { height: 'var(--radix-accordion-content-height)' }
57 | },
58 | 'accordion-up': {
59 | from: { height: 'var(--radix-accordion-content-height)' },
60 | to: { height: '0' }
61 | }
62 | },
63 | animation: {
64 | 'accordion-down': 'accordion-down 0.2s ease-out',
65 | 'accordion-up': 'accordion-up 0.2s ease-out'
66 | }
67 | }
68 | },
69 | plugins: [require('tailwindcss-animate')]
70 | }
71 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }],
4 | "compilerOptions": {
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": [
8 | "./src/renderer/src/*"
9 | ]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/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 | "@/*": [
15 | "./src/renderer/src/*"
16 | ],
17 | "@renderer/*": [
18 | "src/renderer/src/*"
19 | ]
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------