├── .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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/renderer/src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | -------------------------------------------------------------------------------- /src/renderer/src/assets/wavy-lines.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 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 | 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 | --------------------------------------------------------------------------------