├── .editorconfig ├── .erb ├── configs │ ├── .eslintrc │ ├── webpack.config.base.ts │ ├── webpack.config.eslint.ts │ ├── webpack.config.main.dev.ts │ ├── webpack.config.main.prod.ts │ ├── webpack.config.preload.dev.ts │ ├── webpack.config.renderer.dev.dll.ts │ ├── webpack.config.renderer.dev.ts │ ├── webpack.config.renderer.prod.ts │ └── webpack.paths.ts ├── img │ ├── erb-banner.svg │ ├── erb-logo.png │ └── palette-sponsor-banner.svg ├── mocks │ └── fileMock.js └── scripts │ ├── .eslintrc │ ├── check-build-exists.ts │ ├── check-native-dep.js │ ├── check-node-env.js │ ├── check-port-in-use.js │ ├── clean.js │ ├── delete-source-maps.js │ ├── electron-rebuild.js │ ├── link-modules.ts │ ├── notarize.js │ └── version-bump.js ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── 1-Bug_report.md │ ├── 2-Question.md │ └── 3-Feature_request.md ├── config.yml ├── dependabot.yml ├── stale.yml └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .ncurc ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── assets ├── assets.d.ts ├── build │ ├── background.png │ ├── background@2x.png │ └── icon.png ├── entitlements.mac.plist ├── icon.icns ├── icon.ico ├── icon.png ├── icon.svg ├── icons │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 16x16.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 512x512.png │ ├── icon.ico │ ├── tray-Template.png │ └── tray-Template@2x.png └── sounds │ └── ui-sounds │ ├── alerts │ ├── alarm_gentle.wav │ ├── alert_high-intensity.wav │ ├── alert_simple.wav │ ├── notification_ambient.wav │ ├── notification_decorative-01.wav │ ├── notification_decorative-02.wav │ ├── notification_high-intensity.wav │ ├── notification_simple-01.wav │ ├── notification_simple-02.wav │ └── ringtone_minimal.wav │ ├── heros │ ├── hero_decorative-celebration-01.wav │ ├── hero_decorative-celebration-02.wav │ ├── hero_decorative-celebration-03.wav │ ├── hero_simple-celebration-01.wav │ ├── hero_simple-celebration-02.wav │ └── hero_simple-celebration-03.wav │ ├── primary-system │ ├── navigation_backward-selection-minimal.wav │ ├── navigation_backward-selection.wav │ ├── navigation_forward-selection-minimal.wav │ ├── navigation_forward-selection.wav │ ├── navigation_hover-tap.wav │ ├── navigation_selection-complete-celebration.wav │ ├── state-change_confirm-down.wav │ ├── state-change_confirm-up.wav │ ├── ui_camera-shutter.wav │ ├── ui_lock.wav │ ├── ui_tap-variant-01.wav │ ├── ui_tap-variant-02.wav │ ├── ui_tap-variant-03.wav │ ├── ui_tap-variant-04.wav │ └── ui_unlock.wav │ └── secondary-system │ ├── alert_error-01.wav │ ├── alert_error-02.wav │ ├── alert_error-03.wav │ ├── navigation-cancel.wav │ ├── navigation_transition-left.wav │ ├── navigation_transition-right.wav │ ├── navigation_unavailable-selection.wav │ ├── ui_loading.wav │ └── ui_refresh-feed.wav ├── components.json ├── examples.md ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── release └── app │ ├── package-lock.json │ └── package.json ├── src ├── __tests__ │ └── App.test.tsx ├── components │ ├── .autogenerated_shadcn │ └── ui │ │ ├── .autogenerated_shadcn │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── menubar.tsx │ │ ├── popover.tsx │ │ ├── radio-group.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── slider.tsx │ │ ├── sonner.tsx │ │ ├── switch.tsx │ │ └── textarea.tsx ├── config │ ├── config.ts │ ├── exit-codes.ts │ ├── ipc-channels.ts │ ├── keys.ts │ ├── settings.ts │ └── strings.ts ├── hooks │ ├── use-mobile.tsx │ └── use-toast.ts ├── lib │ └── utils.ts ├── main │ ├── analytics.ts │ ├── app-flags.ts │ ├── app-listeners.ts │ ├── auto-update.ts │ ├── context-menu.ts │ ├── create-window.ts │ ├── debugging.ts │ ├── dialog.ts │ ├── dock.ts │ ├── error-handling.ts │ ├── ipc.ts │ ├── keyboard.ts │ ├── logger.ts │ ├── main.ts │ ├── menu-items.ts │ ├── menu.ts │ ├── notifications.ts │ ├── paths.ts │ ├── preload.ts │ ├── protocol.ts │ ├── reset.ts │ ├── sounds.ts │ ├── startup.ts │ ├── stopEvent.ts │ ├── store-actions.ts │ ├── store.ts │ ├── tray.ts │ ├── util.ts │ ├── utils │ │ ├── menu-utils.ts │ │ └── window-utils.ts │ └── windows.ts ├── renderer │ ├── child.tsx │ ├── components │ │ ├── footer │ │ │ ├── AppStatus.tsx │ │ │ ├── Footer.tsx │ │ │ └── OnlineStatus.tsx │ │ ├── images │ │ │ └── Icons.tsx │ │ ├── input │ │ │ ├── ClearButton.tsx │ │ │ ├── InputCheckbox.tsx │ │ │ ├── InputCheckboxGroup.tsx │ │ │ ├── InputColor.tsx │ │ │ ├── InputColorThrottled.tsx │ │ │ ├── InputComboboxForm.tsx │ │ │ ├── InputKeyboardShortcut.tsx │ │ │ ├── InputMouseKeyboardBind.tsx │ │ │ ├── InputRadioGroup.tsx │ │ │ ├── InputSelectForm.tsx │ │ │ ├── InputSlider.tsx │ │ │ ├── InputSliderThrottled.tsx │ │ │ ├── InputSwitch.tsx │ │ │ └── InputSwitchForm.tsx │ │ ├── layout │ │ │ ├── Layout.tsx │ │ │ ├── MainLayout.tsx │ │ │ └── SettingsLayout.tsx │ │ ├── menu │ │ │ └── Menu.tsx │ │ ├── ui │ │ │ ├── DragHandle.tsx │ │ │ ├── ExternalLink.tsx │ │ │ ├── ModeToggle.tsx │ │ │ ├── ScrollPane.tsx │ │ │ ├── SidebarNav.tsx │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── carousel.tsx │ │ │ ├── chart.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input-otp.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── menubar.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── pagination.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── slider.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── toggle.tsx │ │ │ └── tooltip.tsx │ │ ├── views │ │ │ ├── ErrorPage.tsx │ │ │ ├── Home.tsx │ │ │ └── settings │ │ │ │ ├── SettingsJson.tsx │ │ │ │ ├── about │ │ │ │ ├── CardGithub.tsx │ │ │ │ └── SettingsAbout.tsx │ │ │ │ ├── appearance │ │ │ │ ├── SettingsAppearance.tsx │ │ │ │ └── ThemeForm.tsx │ │ │ │ ├── general │ │ │ │ └── SettingsApplication.tsx │ │ │ │ ├── keyboard │ │ │ │ └── SettingsKeyboard.tsx │ │ │ │ └── notifications │ │ │ │ ├── CardNotifications.tsx │ │ │ │ └── SettingsNotifications.tsx │ │ └── windows │ │ │ ├── child │ │ │ └── ChildApp.tsx │ │ │ └── main │ │ │ ├── App.tsx │ │ │ └── MainApp.tsx │ ├── config │ │ ├── icons.tsx │ │ └── nav.tsx │ ├── context │ │ ├── global-context.tsx │ │ └── theme-context.tsx │ ├── index.ejs │ ├── index.tsx │ ├── lib │ │ ├── color-picker │ │ │ ├── ColorPickerInput.tsx │ │ │ ├── assembly.min.css │ │ │ ├── assembly.ts │ │ │ └── color-picker.css │ │ └── sounds.ts │ ├── styles │ │ ├── CssModuleExample.module.scss │ │ ├── ScrollContainer.module.scss │ │ ├── Sidebar.module.scss │ │ └── globals.scss │ └── types │ │ ├── global.ts │ │ └── preload.ts ├── static │ ├── icons │ │ ├── icon-original.png │ │ ├── icon-small.ico │ │ ├── icon-source.xcf │ │ ├── icon.ico │ │ ├── icon.png │ │ ├── icon@2x.png │ │ ├── icon@3x.png │ │ ├── icon_1080.png │ │ ├── tray-Template.png │ │ └── tray-Template@2x.png │ └── sounds │ │ └── ui-sounds │ │ ├── alerts │ │ ├── alarm_gentle.wav │ │ ├── alert_high-intensity.wav │ │ ├── alert_simple.wav │ │ ├── notification_ambient.wav │ │ ├── notification_decorative-01.wav │ │ ├── notification_decorative-02.wav │ │ ├── notification_high-intensity.wav │ │ ├── notification_simple-01.wav │ │ ├── notification_simple-02.wav │ │ └── ringtone_minimal.wav │ │ ├── heros │ │ ├── hero_decorative-celebration-01.wav │ │ ├── hero_decorative-celebration-02.wav │ │ ├── hero_decorative-celebration-03.wav │ │ ├── hero_simple-celebration-01.wav │ │ ├── hero_simple-celebration-02.wav │ │ └── hero_simple-celebration-03.wav │ │ ├── primary-system │ │ ├── navigation_backward-selection-minimal.wav │ │ ├── navigation_backward-selection.wav │ │ ├── navigation_forward-selection-minimal.wav │ │ ├── navigation_forward-selection.wav │ │ ├── navigation_hover-tap.wav │ │ ├── navigation_selection-complete-celebration.wav │ │ ├── state-change_confirm-down.wav │ │ ├── state-change_confirm-up.wav │ │ ├── ui_camera-shutter.wav │ │ ├── ui_lock.wav │ │ ├── ui_tap-variant-01.wav │ │ ├── ui_tap-variant-02.wav │ │ ├── ui_tap-variant-03.wav │ │ ├── ui_tap-variant-04.wav │ │ └── ui_unlock.wav │ │ └── secondary-system │ │ ├── alert_error-01.wav │ │ ├── alert_error-02.wav │ │ ├── alert_error-03.wav │ │ ├── navigation-cancel.wav │ │ ├── navigation_transition-left.wav │ │ ├── navigation_transition-right.wav │ │ ├── navigation_unavailable-selection.wav │ │ ├── ui_loading.wav │ │ └── ui_refresh-feed.wav ├── types │ ├── app.ts │ ├── electron-log.extend.d.ts │ ├── keyboard.ts │ └── notification.ts └── utils │ ├── arrayShuffle.ts │ ├── debounce.ts │ ├── getOS.ts │ ├── getUUID.ts │ ├── invertColor.ts │ ├── padZero.ts │ ├── stopEvent.ts │ └── throttle.ts ├── tailwind.config.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{yml,yaml}] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /.erb/configs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.base.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import TsconfigPathsPlugins from 'tsconfig-paths-webpack-plugin'; 6 | import webpack from 'webpack'; 7 | import { dependencies as externals } from '../../release/app/package.json'; 8 | import webpackPaths from './webpack.paths'; 9 | 10 | const configuration: webpack.Configuration = { 11 | externals: [...Object.keys(externals || {})], 12 | 13 | stats: 'errors-only', 14 | 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.[jt]sx?$/, 19 | exclude: /node_modules/, 20 | use: { 21 | loader: 'ts-loader', 22 | options: { 23 | // Remove this line to enable type checking in webpack builds 24 | transpileOnly: true, 25 | compilerOptions: { 26 | module: 'esnext', 27 | }, 28 | }, 29 | }, 30 | }, 31 | ], 32 | }, 33 | 34 | output: { 35 | path: webpackPaths.srcPath, 36 | // https://github.com/webpack/webpack/issues/1114 37 | library: { 38 | type: 'commonjs2', 39 | }, 40 | }, 41 | 42 | /** 43 | * Determine the array of extensions that should be used to resolve modules. 44 | */ 45 | resolve: { 46 | extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], 47 | modules: [webpackPaths.srcPath, 'node_modules'], 48 | // There is no need to add aliases here, the paths in tsconfig get mirrored 49 | plugins: [new TsconfigPathsPlugins()], 50 | }, 51 | 52 | plugins: [ 53 | new webpack.EnvironmentPlugin({ 54 | NODE_ENV: 'production', 55 | }), 56 | ], 57 | }; 58 | 59 | export default configuration; 60 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.eslint.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/no-unresolved: off, import/no-self-import: off */ 2 | 3 | module.exports = require('./webpack.config.renderer.dev').default; 4 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.main.dev.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for development electron main process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 8 | import { merge } from 'webpack-merge'; 9 | import checkNodeEnv from '../scripts/check-node-env'; 10 | import baseConfig from './webpack.config.base'; 11 | import webpackPaths from './webpack.paths'; 12 | 13 | // When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's 14 | // at the dev webpack config is not accidentally run in a production environment 15 | if (process.env.NODE_ENV === 'production') { 16 | checkNodeEnv('development'); 17 | } 18 | 19 | const configuration: webpack.Configuration = { 20 | devtool: 'inline-source-map', 21 | 22 | mode: 'development', 23 | 24 | target: 'electron-main', 25 | 26 | entry: { 27 | main: path.join(webpackPaths.srcMainPath, 'main.ts'), 28 | preload: path.join(webpackPaths.srcMainPath, 'preload.ts'), 29 | }, 30 | 31 | output: { 32 | path: webpackPaths.dllPath, 33 | filename: '[name].bundle.dev.js', 34 | library: { 35 | type: 'umd', 36 | }, 37 | }, 38 | 39 | plugins: [ 40 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 41 | // @ts-ignore 42 | new BundleAnalyzerPlugin({ 43 | analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', 44 | analyzerPort: 8888, 45 | }), 46 | 47 | new webpack.DefinePlugin({ 48 | 'process.type': '"browser"', 49 | }), 50 | ], 51 | 52 | /** 53 | * Disables webpack processing of __dirname and __filename. 54 | * If you run the bundle in node.js it falls back to these values of node.js. 55 | * https://github.com/webpack/webpack/issues/2010 56 | */ 57 | node: { 58 | __dirname: false, 59 | __filename: false, 60 | }, 61 | }; 62 | 63 | export default merge(baseConfig, configuration); 64 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.main.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { merge } from 'webpack-merge'; 8 | import TerserPlugin from 'terser-webpack-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import baseConfig from './webpack.config.base'; 11 | import webpackPaths from './webpack.paths'; 12 | import checkNodeEnv from '../scripts/check-node-env'; 13 | import deleteSourceMaps from '../scripts/delete-source-maps'; 14 | 15 | checkNodeEnv('production'); 16 | deleteSourceMaps(); 17 | 18 | const configuration: webpack.Configuration = { 19 | devtool: 'source-map', 20 | 21 | mode: 'production', 22 | 23 | target: 'electron-main', 24 | 25 | entry: { 26 | main: path.join(webpackPaths.srcMainPath, 'main.ts'), 27 | preload: path.join(webpackPaths.srcMainPath, 'preload.ts'), 28 | }, 29 | 30 | output: { 31 | path: webpackPaths.distMainPath, 32 | filename: '[name].js', 33 | library: { 34 | type: 'umd', 35 | }, 36 | }, 37 | 38 | optimization: { 39 | minimizer: [ 40 | new TerserPlugin({ 41 | parallel: true, 42 | }), 43 | ], 44 | }, 45 | 46 | plugins: [ 47 | new BundleAnalyzerPlugin({ 48 | analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', 49 | analyzerPort: 8888, 50 | }), 51 | 52 | /** 53 | * Create global constants which can be configured at compile time. 54 | * 55 | * Useful for allowing different behaviour between development builds and 56 | * release builds 57 | * 58 | * NODE_ENV should be production so that modules do not perform certain 59 | * development checks 60 | */ 61 | new webpack.EnvironmentPlugin({ 62 | NODE_ENV: 'production', 63 | DEBUG_PROD: false, 64 | START_MINIMIZED: false, 65 | }), 66 | 67 | new webpack.DefinePlugin({ 68 | 'process.type': '"browser"', 69 | }), 70 | ], 71 | 72 | /** 73 | * Disables webpack processing of __dirname and __filename. 74 | * If you run the bundle in node.js it falls back to these values of node.js. 75 | * https://github.com/webpack/webpack/issues/2010 76 | */ 77 | node: { 78 | __dirname: false, 79 | __filename: false, 80 | }, 81 | }; 82 | 83 | export default merge(baseConfig, configuration); 84 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.preload.dev.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import webpack from 'webpack'; 3 | import { merge } from 'webpack-merge'; 4 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 5 | import baseConfig from './webpack.config.base'; 6 | import webpackPaths from './webpack.paths'; 7 | import checkNodeEnv from '../scripts/check-node-env'; 8 | 9 | // When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's 10 | // at the dev webpack config is not accidentally run in a production environment 11 | if (process.env.NODE_ENV === 'production') { 12 | checkNodeEnv('development'); 13 | } 14 | 15 | const configuration: webpack.Configuration = { 16 | devtool: 'inline-source-map', 17 | 18 | mode: 'development', 19 | 20 | target: 'electron-preload', 21 | 22 | entry: path.join(webpackPaths.srcMainPath, 'preload.ts'), 23 | 24 | output: { 25 | path: webpackPaths.dllPath, 26 | filename: 'preload.js', 27 | library: { 28 | type: 'umd', 29 | }, 30 | }, 31 | 32 | plugins: [ 33 | new BundleAnalyzerPlugin({ 34 | analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', 35 | }), 36 | 37 | /** 38 | * Create global constants which can be configured at compile time. 39 | * 40 | * Useful for allowing different behaviour between development builds and 41 | * release builds 42 | * 43 | * NODE_ENV should be production so that modules do not perform certain 44 | * development checks 45 | * 46 | * By default, use 'development' as NODE_ENV. This can be overriden with 47 | * 'staging', for example, by changing the ENV variables in the npm scripts 48 | */ 49 | new webpack.EnvironmentPlugin({ 50 | NODE_ENV: 'development', 51 | }), 52 | 53 | new webpack.LoaderOptionsPlugin({ 54 | debug: true, 55 | }), 56 | ], 57 | 58 | /** 59 | * Disables webpack processing of __dirname and __filename. 60 | * If you run the bundle in node.js it falls back to these values of node.js. 61 | * https://github.com/webpack/webpack/issues/2010 62 | */ 63 | node: { 64 | __dirname: false, 65 | __filename: false, 66 | }, 67 | 68 | watch: true, 69 | }; 70 | 71 | export default merge(baseConfig, configuration); 72 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.renderer.dev.dll.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Builds the DLL for development electron renderer process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import path from 'path'; 7 | import { merge } from 'webpack-merge'; 8 | import baseConfig from './webpack.config.base'; 9 | import webpackPaths from './webpack.paths'; 10 | import { dependencies } from '../../package.json'; 11 | import checkNodeEnv from '../scripts/check-node-env'; 12 | 13 | checkNodeEnv('development'); 14 | 15 | const dist = webpackPaths.dllPath; 16 | 17 | const configuration: webpack.Configuration = { 18 | context: webpackPaths.rootPath, 19 | 20 | devtool: 'eval', 21 | 22 | mode: 'development', 23 | 24 | target: 'electron-renderer', 25 | 26 | externals: ['fsevents', 'crypto-browserify'], 27 | 28 | /** 29 | * Use `module` from `webpack.config.renderer.dev.js` 30 | */ 31 | module: require('./webpack.config.renderer.dev').default.module, 32 | 33 | entry: { 34 | renderer: Object.keys(dependencies || {}), 35 | }, 36 | 37 | output: { 38 | path: dist, 39 | filename: '[name].dev.dll.js', 40 | library: { 41 | name: 'renderer', 42 | type: 'var', 43 | }, 44 | }, 45 | 46 | plugins: [ 47 | new webpack.DllPlugin({ 48 | path: path.join(dist, '[name].json'), 49 | name: '[name]', 50 | }), 51 | 52 | /** 53 | * Create global constants which can be configured at compile time. 54 | * 55 | * Useful for allowing different behaviour between development builds and 56 | * release builds 57 | * 58 | * NODE_ENV should be production so that modules do not perform certain 59 | * development checks 60 | */ 61 | new webpack.EnvironmentPlugin({ 62 | NODE_ENV: 'development', 63 | }), 64 | 65 | new webpack.LoaderOptionsPlugin({ 66 | debug: true, 67 | options: { 68 | context: webpackPaths.srcPath, 69 | output: { 70 | path: webpackPaths.dllPath, 71 | }, 72 | }, 73 | }), 74 | ], 75 | }; 76 | 77 | export default merge(baseConfig, configuration); 78 | -------------------------------------------------------------------------------- /.erb/configs/webpack.paths.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.join(__dirname, '../..'); 4 | 5 | const erbPath = path.join(__dirname, '..'); 6 | const erbNodeModulesPath = path.join(erbPath, 'node_modules'); 7 | 8 | const dllPath = path.join(__dirname, '../dll'); 9 | 10 | const srcPath = path.join(rootPath, 'src'); 11 | const srcMainPath = path.join(srcPath, 'main'); 12 | const srcRendererPath = path.join(srcPath, 'renderer'); 13 | 14 | const releasePath = path.join(rootPath, 'release'); 15 | const appPath = path.join(releasePath, 'app'); 16 | const appPackagePath = path.join(appPath, 'package.json'); 17 | const appNodeModulesPath = path.join(appPath, 'node_modules'); 18 | const srcNodeModulesPath = path.join(srcPath, 'node_modules'); 19 | 20 | const distPath = path.join(appPath, 'dist'); 21 | const distMainPath = path.join(distPath, 'main'); 22 | const distRendererPath = path.join(distPath, 'renderer'); 23 | 24 | const buildPath = path.join(releasePath, 'build'); 25 | 26 | export default { 27 | rootPath, 28 | erbNodeModulesPath, 29 | dllPath, 30 | srcPath, 31 | srcMainPath, 32 | srcRendererPath, 33 | releasePath, 34 | appPath, 35 | appPackagePath, 36 | appNodeModulesPath, 37 | srcNodeModulesPath, 38 | distPath, 39 | distMainPath, 40 | distRendererPath, 41 | buildPath, 42 | }; 43 | -------------------------------------------------------------------------------- /.erb/img/erb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/.erb/img/erb-logo.png -------------------------------------------------------------------------------- /.erb/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /.erb/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off", 6 | "import/no-extraneous-dependencies": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.erb/scripts/check-build-exists.ts: -------------------------------------------------------------------------------- 1 | // Check if the renderer and main bundles are built 2 | import path from 'path'; 3 | import chalk from 'chalk'; 4 | import fs from 'fs'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | const mainPath = path.join(webpackPaths.distMainPath, 'main.js'); 8 | const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js'); 9 | 10 | if (!fs.existsSync(mainPath)) { 11 | throw new Error( 12 | chalk.whiteBright.bgRed.bold( 13 | 'The main process is not built yet. Build it by running "npm run build:main"', 14 | ), 15 | ); 16 | } 17 | 18 | if (!fs.existsSync(rendererPath)) { 19 | throw new Error( 20 | chalk.whiteBright.bgRed.bold( 21 | 'The renderer process is not built yet. Build it by running "npm run build:renderer"', 22 | ), 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /.erb/scripts/check-native-dep.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import chalk from 'chalk'; 3 | import { execSync } from 'child_process'; 4 | import { dependencies } from '../../package.json'; 5 | 6 | if (dependencies) { 7 | const dependenciesKeys = Object.keys(dependencies); 8 | const nativeDeps = fs 9 | .readdirSync('node_modules') 10 | .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); 11 | if (nativeDeps.length === 0) { 12 | process.exit(0); 13 | } 14 | try { 15 | // Find the reason for why the dependency is installed. If it is installed 16 | // because of a devDependency then that is okay. Warn when it is installed 17 | // because of a dependency 18 | const { dependencies: dependenciesObject } = JSON.parse( 19 | execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString(), 20 | ); 21 | const rootDependencies = Object.keys(dependenciesObject); 22 | const filteredRootDependencies = rootDependencies.filter((rootDependency) => 23 | dependenciesKeys.includes(rootDependency), 24 | ); 25 | if (filteredRootDependencies.length > 0) { 26 | const plural = filteredRootDependencies.length > 1; 27 | console.log(` 28 | ${chalk.whiteBright.bgYellow.bold( 29 | 'Webpack does not work with native dependencies.', 30 | )} 31 | ${chalk.bold(filteredRootDependencies.join(', '))} ${ 32 | plural ? 'are native dependencies' : 'is a native dependency' 33 | } and should be installed inside of the "./release/app" folder. 34 | First, uninstall the packages from "./package.json": 35 | ${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} 36 | ${chalk.bold( 37 | 'Then, instead of installing the package to the root "./package.json":', 38 | )} 39 | ${chalk.whiteBright.bgRed.bold('npm install your-package')} 40 | ${chalk.bold('Install the package to "./release/app/package.json"')} 41 | ${chalk.whiteBright.bgGreen.bold( 42 | 'cd ./release/app && npm install your-package', 43 | )} 44 | Read more about native dependencies at: 45 | ${chalk.bold( 46 | 'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure', 47 | )} 48 | `); 49 | process.exit(1); 50 | } 51 | } catch (e) { 52 | console.log('Native dependencies could not be checked'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.erb/scripts/check-node-env.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default function checkNodeEnv(expectedEnv) { 4 | if (!expectedEnv) { 5 | throw new Error('"expectedEnv" not set'); 6 | } 7 | 8 | if (process.env.NODE_ENV !== expectedEnv) { 9 | console.log( 10 | chalk.whiteBright.bgRed.bold( 11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`, 12 | ), 13 | ); 14 | process.exit(2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.erb/scripts/check-port-in-use.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import detectPort from 'detect-port'; 3 | 4 | const port = process.env.PORT || '1212'; 5 | 6 | detectPort(port, (_err, availablePort) => { 7 | if (port !== String(availablePort)) { 8 | throw new Error( 9 | chalk.whiteBright.bgRed.bold( 10 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start`, 11 | ), 12 | ); 13 | } else { 14 | process.exit(0); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /.erb/scripts/clean.js: -------------------------------------------------------------------------------- 1 | import { rimrafSync } from 'rimraf'; 2 | import fs from 'fs'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | const foldersToRemove = [ 6 | webpackPaths.distPath, 7 | webpackPaths.buildPath, 8 | webpackPaths.dllPath, 9 | ]; 10 | 11 | foldersToRemove.forEach((folder) => { 12 | if (fs.existsSync(folder)) rimrafSync(folder); 13 | }); 14 | -------------------------------------------------------------------------------- /.erb/scripts/delete-source-maps.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import { rimrafSync } from 'rimraf'; 4 | import webpackPaths from '../configs/webpack.paths'; 5 | 6 | export default function deleteSourceMaps() { 7 | if (fs.existsSync(webpackPaths.distMainPath)) 8 | rimrafSync(path.join(webpackPaths.distMainPath, '*.js.map'), { 9 | glob: true, 10 | }); 11 | if (fs.existsSync(webpackPaths.distRendererPath)) 12 | rimrafSync(path.join(webpackPaths.distRendererPath, '*.js.map'), { 13 | glob: true, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /.erb/scripts/electron-rebuild.js: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | import fs from 'fs'; 3 | import { dependencies } from '../../release/app/package.json'; 4 | import webpackPaths from '../configs/webpack.paths'; 5 | 6 | if ( 7 | Object.keys(dependencies || {}).length > 0 && 8 | fs.existsSync(webpackPaths.appNodeModulesPath) 9 | ) { 10 | const electronRebuildCmd = 11 | '../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .'; 12 | const cmd = 13 | process.platform === 'win32' 14 | ? electronRebuildCmd.replace(/\//g, '\\') 15 | : electronRebuildCmd; 16 | execSync(cmd, { 17 | cwd: webpackPaths.appPath, 18 | stdio: 'inherit', 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /.erb/scripts/link-modules.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import webpackPaths from '../configs/webpack.paths'; 3 | 4 | const { srcNodeModulesPath, appNodeModulesPath, erbNodeModulesPath } = 5 | webpackPaths; 6 | 7 | if (fs.existsSync(appNodeModulesPath)) { 8 | if (!fs.existsSync(srcNodeModulesPath)) { 9 | fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); 10 | } 11 | if (!fs.existsSync(erbNodeModulesPath)) { 12 | fs.symlinkSync(appNodeModulesPath, erbNodeModulesPath, 'junction'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.erb/scripts/notarize.js: -------------------------------------------------------------------------------- 1 | const { notarize } = require('@electron/notarize'); 2 | const { build } = require('../../package.json'); 3 | 4 | exports.default = async function notarizeMacos(context) { 5 | const { electronPlatformName, appOutDir } = context; 6 | if (electronPlatformName !== 'darwin') { 7 | return; 8 | } 9 | 10 | if (process.env.CI !== 'true') { 11 | console.warn('Skipping notarizing step. Packaging is not running in CI'); 12 | return; 13 | } 14 | 15 | if ( 16 | !( 17 | 'APPLE_ID' in process.env && 18 | 'APPLE_ID_PASS' in process.env && 19 | 'APPLE_TEAM_ID' in process.env 20 | ) 21 | ) { 22 | console.warn( 23 | 'Skipping notarizing step. APPLE_ID, APPLE_ID_PASS, and APPLE_TEAM_ID env variables must be set', 24 | ); 25 | return; 26 | } 27 | 28 | const appName = context.packager.appInfo.productFilename; 29 | 30 | await notarize({ 31 | tool: 'notarytool', 32 | appBundleId: build.appId, 33 | appPath: `${appOutDir}/${appName}.app`, 34 | appleId: process.env.APPLE_ID, 35 | appleIdPassword: process.env.APPLE_ID_PASS, 36 | teamId: process.env.APPLE_TEAM_ID, 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /.erb/scripts/version-bump.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { execSync } = require('child_process'); 4 | 5 | const rootPackagePath = path.join(__dirname, '..', '..', 'package.json'); 6 | const appPackagePath = path.join( 7 | __dirname, 8 | '..', 9 | '..', 10 | 'release', 11 | 'app', 12 | 'package.json', 13 | ); 14 | 15 | function updateVersion(type) { 16 | // Update root package.json 17 | execSync(`npm version ${type} --no-git-tag-version`); 18 | 19 | // Read the new version from root package.json 20 | const rootPackage = JSON.parse(fs.readFileSync(rootPackagePath, 'utf8')); 21 | const newVersion = rootPackage.version; 22 | 23 | // Update app package.json 24 | const appPackage = JSON.parse(fs.readFileSync(appPackagePath, 'utf8')); 25 | appPackage.version = newVersion; 26 | fs.writeFileSync(appPackagePath, JSON.stringify(appPackage, null, 2)); 27 | 28 | console.log(`Version bumped to ${newVersion}`); 29 | 30 | // Stage changes 31 | execSync('git add package.json release/app/package.json'); 32 | 33 | // Commit changes 34 | execSync(`git commit -m "Bump version to ${newVersion}"`); 35 | } 36 | 37 | const versionType = process.argv[2]; 38 | if (!['patch', 'minor', 'major'].includes(versionType)) { 39 | console.error('Please specify version type: patch, minor, or major'); 40 | process.exit(1); 41 | } 42 | 43 | updateVersion(versionType); 44 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | npm-debug.log.* 27 | *.css.d.ts 28 | *.sass.d.ts 29 | *.scss.d.ts 30 | 31 | # eslint ignores hidden directories by default: 32 | # https://github.com/eslint/eslint/issues/8429 33 | *.ejs 34 | !.erb 35 | 36 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'erb', 3 | plugins: ['@typescript-eslint'], 4 | // Ignore shadcn/ui components 5 | ignorePatterns: ['**/components/ui/**', '**/renderer/lib/**'], 6 | rules: { 7 | // A temporary hack related to IDE not resolving correct package.json 8 | // 'import/no-extraneous-dependencies': 'off', 9 | 'react/react-in-jsx-scope': 'off', 10 | 'react/jsx-filename-extension': 'off', 11 | 'import/extensions': 'off', 12 | 'import/no-unresolved': 'off', 13 | 'import/no-import-module-exports': 'off', 14 | 'no-shadow': 'off', 15 | '@typescript-eslint/no-shadow': 'error', 16 | 'no-unused-vars': 'off', 17 | 18 | // Added in Electron-Hotplate 19 | '@typescript-eslint/no-unused-vars': [ 20 | 'warn', 21 | { 22 | vars: 'all', 23 | varsIgnorePattern: '^_', 24 | args: 'after-used', 25 | argsIgnorePattern: '^_', 26 | }, 27 | ], 28 | 'consistent-return': 'off', 29 | 'import/prefer-default-export': 'off', 30 | 'promise/always-return': 'off', 31 | 'react/jsx-props-no-spreading': 'off', 32 | 'react/jsx-no-useless-fragment': 'off', 33 | 'react/prop-types': 'off', 34 | 'react/require-default-props': 'off', 35 | }, 36 | parserOptions: { 37 | ecmaVersion: 2022, 38 | sourceType: 'module', 39 | }, 40 | settings: { 41 | 'import/resolver': { 42 | // See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below 43 | node: { 44 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 45 | moduleDirectory: ['node_modules', 'src/'], 46 | }, 47 | webpack: { 48 | config: require.resolve('./.erb/configs/webpack.config.eslint.ts'), 49 | }, 50 | typescript: {}, 51 | }, 52 | 'import/parsers': { 53 | '@typescript-eslint/parser': ['.ts', '.tsx'], 54 | }, 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.exe binary 3 | *.png binary 4 | *.jpg binary 5 | *.jpeg binary 6 | *.ico binary 7 | *.icns binary 8 | *.eot binary 9 | *.otf binary 10 | *.ttf binary 11 | *.woff binary 12 | *.woff2 binary 13 | *.wav binary 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: lacymorrow 4 | patreon: lacymorrow 5 | open_collective: crossover 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://www.buymeacoffee.com/lm 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: You're having technical issues. 🐞 4 | labels: 'bug' 5 | --- 6 | 7 | ## Expected Behavior 8 | 9 | 10 | 11 | ## Current Behavior 12 | 13 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-Question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question.❓ 4 | labels: 'question' 5 | --- 6 | 7 | ## Summary 8 | 9 | 10 | 11 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: You want something added. 🎉 4 | labels: 'enhancement' 5 | --- 6 | 7 | 16 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | requiredHeaders: 2 | - Prerequisites 3 | - Expected Behavior 4 | - Current Behavior 5 | - Possible Solution 6 | - Your Environment 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: 'npm' # See documentation for possible values 9 | directory: '/' # Location of package manifests 10 | schedule: 11 | interval: 'weekly' 12 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 90 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 30 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - discussion 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-mac-windows: 10 | # To enable auto publishing to github, update your electron publisher 11 | # config in package.json > "build" and remove the conditional below 12 | if: ${{ github.repository_owner == 'lacymorrow' }} 13 | 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | os: [macos-latest] 19 | 20 | steps: 21 | - name: Checkout git repo 22 | uses: actions/checkout@v3 23 | 24 | - name: Install Node and NPM 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: 18 28 | cache: npm 29 | 30 | - name: Install and build 31 | run: | 32 | npm install 33 | npm run postinstall 34 | npm run package:mw 35 | 36 | - name: Archive build artifacts 37 | uses: actions/upload-artifact@v3 38 | with: 39 | name: release 40 | path: | 41 | release/build 42 | !release/**/builder* 43 | !release/**/linux* 44 | !release/**/mac* 45 | !release/**/win* 46 | 47 | build-linux: 48 | # To enable auto publishing to github, update your electron publisher 49 | # config in package.json > "build" and remove the conditional below 50 | if: ${{ github.repository_owner == 'lacymorrow' }} 51 | 52 | runs-on: ${{ matrix.os }} 53 | 54 | strategy: 55 | matrix: 56 | os: [ubuntu-latest] 57 | 58 | steps: 59 | - name: Checkout git repo 60 | uses: actions/checkout@v3 61 | 62 | - name: Install Node and NPM 63 | uses: actions/setup-node@v3 64 | with: 65 | node-version: 18 66 | cache: npm 67 | 68 | - name: Install dependencies 69 | run: sudo apt install libarchive-tools 70 | 71 | - name: Install and build 72 | run: | 73 | npm install 74 | npm run postinstall 75 | npm run package:linux 76 | 77 | - name: Archive build artifacts 78 | uses: actions/upload-artifact@v3 79 | with: 80 | name: release 81 | path: | 82 | release/build 83 | !release/**/builder* 84 | !release/**/linux* 85 | !release/**/mac* 86 | !release/**/win* 87 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | publish: 10 | # To enable auto publishing to github, update your electron publisher 11 | # config in package.json > "build" and remove the conditional below 12 | # MAKE SURE TO REMOVE THE EXCLAMATION MARK FROM THE OWNER NAME BELOW 13 | if: ${{ github.repository_owner == 'lacymorrow' }} 14 | 15 | runs-on: ${{ matrix.os }} 16 | 17 | strategy: 18 | matrix: 19 | # TODO: Add windows and linux to the matrix, We only use one right now to prevent publishing more than once 20 | os: [macos-latest] 21 | 22 | steps: 23 | - name: Checkout git repo 24 | uses: actions/checkout@v3 25 | 26 | - name: Install Node and NPM 27 | uses: actions/setup-node@v3 28 | with: 29 | node-version: 18 30 | cache: npm 31 | 32 | - name: Install and build 33 | run: | 34 | npm install 35 | npm run postinstall 36 | npm run build 37 | 38 | - name: Publish releases 39 | env: 40 | # The APPLE_* values are used for auto updates signing 41 | APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASS }} 42 | APPLE_ID: ${{ secrets.APPLE_ID }} 43 | APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} 44 | CSC_LINK: ${{ secrets.CSC_LINK }} 45 | CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} 46 | # This is used for uploading release assets to github 47 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | run: | 49 | npm exec electron-builder -- --publish always --win --mac --linux 50 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ${{ matrix.os }} 8 | 9 | strategy: 10 | matrix: 11 | os: [macos-latest, windows-latest, ubuntu-latest] 12 | 13 | steps: 14 | - name: Check out Git repository 15 | uses: actions/checkout@v3 16 | 17 | - name: Install Node.js and NPM 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 18 21 | cache: npm 22 | 23 | - name: npm install 24 | run: | 25 | npm install 26 | 27 | - name: npm test 28 | env: 29 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | run: | 31 | npm run lint 32 | npm exec tsc 33 | npm test 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | npm-debug.log.* 27 | *.css.d.ts 28 | *.sass.d.ts 29 | *.scss.d.ts 30 | -------------------------------------------------------------------------------- /.ncurc: -------------------------------------------------------------------------------- 1 | { 2 | "reject": [ 3 | "chalk", 4 | "css-loader", 5 | "electron", 6 | "electron-store", 7 | "electron-debug", 8 | "eslint" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | enable-pre-post-scripts=true 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "EditorConfig.EditorConfig", 5 | "ecmel.vscode-html-css", 6 | "bradlc.vscode-tailwindcss", 7 | "formulahendry.auto-rename-tag", 8 | "michelemelluso.code-beautifier", 9 | "aeschli.vscode-css-formatter", 10 | "mikestead.dotenv", 11 | "github.vscode-github-actions" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Electron: Main", 6 | "type": "node", 7 | "request": "launch", 8 | "protocol": "inspector", 9 | "runtimeExecutable": "npm", 10 | "runtimeArgs": ["run", "start"], 11 | "env": { 12 | "MAIN_ARGS": "--inspect=5858 --remote-debugging-port=9223" 13 | } 14 | }, 15 | { 16 | "name": "Electron: Renderer", 17 | "type": "chrome", 18 | "request": "attach", 19 | "port": 9223, 20 | "webRoot": "${workspaceFolder}", 21 | "timeout": 15000 22 | } 23 | ], 24 | "compounds": [ 25 | { 26 | "name": "Electron: All", 27 | "configurations": ["Electron: Main", "Electron: Renderer"] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | ".eslintrc": "jsonc", 4 | ".prettierrc": "jsonc", 5 | ".eslintignore": "ignore" 6 | }, 7 | 8 | "eslint.validate": [ 9 | "javascript", 10 | "javascriptreact", 11 | "html", 12 | "typescriptreact" 13 | ], 14 | 15 | "search.exclude": { 16 | ".git": true, 17 | ".eslintcache": true, 18 | ".erb/dll": true, 19 | "release/{build,app/dist}": true, 20 | "node_modules": true, 21 | "npm-debug.log.*": true, 22 | "test/**/__snapshots__": true, 23 | "package-lock.json": true, 24 | "*.{css,sass,scss}.d.ts": true 25 | }, 26 | "compile-hero.disable-compile-files-on-did-save-code": true 27 | } 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 3.0.0 2 | 3 | Rebuild from scratch using Electron and React. This is a complete rewrite of the original app. 4 | -------------------------------------------------------------------------------- /assets/assets.d.ts: -------------------------------------------------------------------------------- 1 | type Styles = Record; 2 | 3 | declare module '*.svg' { 4 | import React = require('react'); 5 | 6 | export const ReactComponent: React.FC>; 7 | 8 | const content: string; 9 | export default content; 10 | } 11 | 12 | declare module '*.png' { 13 | const content: string; 14 | export default content; 15 | } 16 | 17 | declare module '*.jpg' { 18 | const content: string; 19 | export default content; 20 | } 21 | 22 | declare module '*.scss' { 23 | const content: Styles; 24 | export default content; 25 | } 26 | 27 | declare module '*.sass' { 28 | const content: Styles; 29 | export default content; 30 | } 31 | 32 | declare module '*.css' { 33 | const content: Styles; 34 | export default content; 35 | } 36 | -------------------------------------------------------------------------------- /assets/build/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/build/background.png -------------------------------------------------------------------------------- /assets/build/background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/build/background@2x.png -------------------------------------------------------------------------------- /assets/build/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/build/icon.png -------------------------------------------------------------------------------- /assets/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icon.icns -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icon.ico -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icon.png -------------------------------------------------------------------------------- /assets/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/1024x1024.png -------------------------------------------------------------------------------- /assets/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/128x128.png -------------------------------------------------------------------------------- /assets/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/16x16.png -------------------------------------------------------------------------------- /assets/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/256x256.png -------------------------------------------------------------------------------- /assets/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/32x32.png -------------------------------------------------------------------------------- /assets/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/512x512.png -------------------------------------------------------------------------------- /assets/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/icon.ico -------------------------------------------------------------------------------- /assets/icons/tray-Template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/tray-Template.png -------------------------------------------------------------------------------- /assets/icons/tray-Template@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/icons/tray-Template@2x.png -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/alarm_gentle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/alarm_gentle.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/alert_high-intensity.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/alert_high-intensity.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/alert_simple.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/alert_simple.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/notification_ambient.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/notification_ambient.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/notification_decorative-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/notification_decorative-01.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/notification_decorative-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/notification_decorative-02.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/notification_high-intensity.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/notification_high-intensity.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/notification_simple-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/notification_simple-01.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/notification_simple-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/notification_simple-02.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/alerts/ringtone_minimal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/alerts/ringtone_minimal.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/heros/hero_decorative-celebration-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/heros/hero_decorative-celebration-01.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/heros/hero_decorative-celebration-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/heros/hero_decorative-celebration-02.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/heros/hero_decorative-celebration-03.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/heros/hero_decorative-celebration-03.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/heros/hero_simple-celebration-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/heros/hero_simple-celebration-01.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/heros/hero_simple-celebration-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/heros/hero_simple-celebration-02.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/heros/hero_simple-celebration-03.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/heros/hero_simple-celebration-03.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/navigation_backward-selection-minimal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/navigation_backward-selection-minimal.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/navigation_backward-selection.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/navigation_backward-selection.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/navigation_forward-selection-minimal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/navigation_forward-selection-minimal.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/navigation_forward-selection.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/navigation_forward-selection.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/navigation_hover-tap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/navigation_hover-tap.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/navigation_selection-complete-celebration.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/navigation_selection-complete-celebration.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/state-change_confirm-down.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/state-change_confirm-down.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/state-change_confirm-up.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/state-change_confirm-up.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_camera-shutter.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_camera-shutter.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_lock.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_lock.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_tap-variant-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_tap-variant-01.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_tap-variant-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_tap-variant-02.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_tap-variant-03.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_tap-variant-03.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_tap-variant-04.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_tap-variant-04.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/primary-system/ui_unlock.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/primary-system/ui_unlock.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/alert_error-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/alert_error-01.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/alert_error-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/alert_error-02.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/alert_error-03.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/alert_error-03.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/navigation-cancel.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/navigation-cancel.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/navigation_transition-left.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/navigation_transition-left.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/navigation_transition-right.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/navigation_transition-right.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/navigation_unavailable-selection.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/navigation_unavailable-selection.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/ui_loading.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/ui_loading.wav -------------------------------------------------------------------------------- /assets/sounds/ui-sounds/secondary-system/ui_refresh-feed.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shipkit-io/electron-bones/763613dedd95d72dd428eebc751cace24be74464/assets/sounds/ui-sounds/secondary-system/ui_refresh-feed.wav -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/renderer/styles/globals.scss", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components/blocks", 15 | "ui": "@/components/ui", 16 | "utils": "@/lib/utils", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "radix" 21 | } 22 | -------------------------------------------------------------------------------- /examples.md: -------------------------------------------------------------------------------- 1 | # Electron Examples 2 | 3 | From the [Electron Docs](https://www.electronjs.org/docs) 4 | 5 | ## Drag and Drop 6 | 7 | https://www.electronjs.org/docs/latest/tutorial/native-file-drag-drop 8 | 9 | ## Progress Bar 10 | 11 | ```js 12 | windows.mainWindow.setProgressBar(c) 13 | ``` 14 | 15 | ## Recent Documents 16 | 17 | ```js 18 | app.addRecentDocument(path) 19 | 20 | app.clearRecentDocuments() 21 | ``` 22 | 23 | ## File Associations 24 | 25 | ```js 26 | app.setAsDefaultProtocolClient(protocol[, path, args]) 27 | ``` 28 | 29 | ## Spell Check 30 | 31 | ```js 32 | webPreferences: { 33 | spellcheck: true 34 | } 35 | ``` 36 | 37 | Spell check context menu 38 | 39 | ```js 40 | myWindow.webContents.on('context-menu', (event, params) => { 41 | const menu = new Menu() 42 | 43 | // Add each spelling suggestion 44 | for (const suggestion of params.dictionarySuggestions) { 45 | menu.append(new MenuItem({ 46 | label: suggestion, 47 | click: () => myWindow.webContents.replaceMisspelling(suggestion) 48 | })) 49 | } 50 | 51 | // Allow users to add the misspelled word to the dictionary 52 | if (params.misspelledWord) { 53 | menu.append( 54 | new MenuItem({ 55 | label: 'Add to dictionary', 56 | click: () => myWindow.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord) 57 | }) 58 | ) 59 | } 60 | 61 | menu.popup() 62 | }) 63 | ``` 64 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'tailwindcss/nesting': {}, 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /release/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-bones", 3 | "version": "4.6.0", 4 | "description": "A batteries-included Electron boilerplate.", 5 | "license": "CC-BY-NC-SA-4.0", 6 | "homepage": "https://lacymorrow.github.io/electron-bones", 7 | "author": { 8 | "name": "Lacy Morrow", 9 | "email": "me@lacymorrow.com", 10 | "url": "https://github.com/lacymorrow/electron-bones" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/lacymorrow/electron-bones/issues", 14 | "email": "me@lacymorrow.com" 15 | }, 16 | "main": "./dist/main/main.js", 17 | "scripts": { 18 | "rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js", 19 | "postinstall": "npm run rebuild && npm run link-modules", 20 | "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts" 21 | }, 22 | "dependencies": { 23 | "@aptabase/electron": "^0.3.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import { render } from '@testing-library/react'; 3 | import App from '../renderer/components/windows/main/App'; 4 | 5 | describe('App', () => { 6 | it('should render', () => { 7 | expect(render()).toBeTruthy(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/.autogenerated_shadcn: -------------------------------------------------------------------------------- 1 | # https://ui.shadcn.com/ 2 | -------------------------------------------------------------------------------- /src/components/ui/.autogenerated_shadcn: -------------------------------------------------------------------------------- 1 | # https://ui.shadcn.com/ 2 | -------------------------------------------------------------------------------- /src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ) 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ) 34 | } 35 | 36 | export { Badge, badgeVariants } 37 | -------------------------------------------------------------------------------- /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 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", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

41 | )) 42 | CardTitle.displayName = "CardTitle" 43 | 44 | const CardDescription = React.forwardRef< 45 | HTMLParagraphElement, 46 | React.HTMLAttributes 47 | >(({ className, ...props }, ref) => ( 48 |

53 | )) 54 | CardDescription.displayName = "CardDescription" 55 | 56 | const CardContent = React.forwardRef< 57 | HTMLDivElement, 58 | React.HTMLAttributes 59 | >(({ className, ...props }, ref) => ( 60 |

61 | )) 62 | CardContent.displayName = "CardContent" 63 | 64 | const CardFooter = React.forwardRef< 65 | HTMLDivElement, 66 | React.HTMLAttributes 67 | >(({ className, ...props }, ref) => ( 68 |
73 | )) 74 | CardFooter.displayName = "CardFooter" 75 | 76 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 77 | -------------------------------------------------------------------------------- /src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 3 | import { CheckIcon } from "@radix-ui/react-icons" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const Checkbox = React.forwardRef< 8 | React.ElementRef, 9 | React.ComponentPropsWithoutRef 10 | >(({ className, ...props }, ref) => ( 11 | 19 | 22 | 23 | 24 | 25 | )) 26 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 27 | 28 | export { Checkbox } 29 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const labelVariants = cva( 8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 9 | ) 10 | 11 | const Label = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef & 14 | VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 21 | )) 22 | Label.displayName = LabelPrimitive.Root.displayName 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /src/components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as PopoverPrimitive from "@radix-ui/react-popover" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Popover = PopoverPrimitive.Root 7 | 8 | const PopoverTrigger = PopoverPrimitive.Trigger 9 | 10 | const PopoverAnchor = PopoverPrimitive.Anchor 11 | 12 | const PopoverContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 17 | 27 | 28 | )) 29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName 30 | 31 | export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } 32 | -------------------------------------------------------------------------------- /src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { CheckIcon } from "@radix-ui/react-icons" 3 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const RadioGroup = React.forwardRef< 8 | React.ElementRef, 9 | React.ComponentPropsWithoutRef 10 | >(({ className, ...props }, ref) => { 11 | return ( 12 | 17 | ) 18 | }) 19 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 20 | 21 | const RadioGroupItem = React.forwardRef< 22 | React.ElementRef, 23 | React.ComponentPropsWithoutRef 24 | >(({ className, ...props }, ref) => { 25 | return ( 26 | 34 | 35 | 36 | 37 | 38 | ) 39 | }) 40 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 41 | 42 | export { RadioGroup, RadioGroupItem } 43 | -------------------------------------------------------------------------------- /src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; 2 | import * as React from 'react'; 3 | 4 | import { cn } from '@/lib/utils'; 5 | 6 | const ScrollArea = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, children, ...props }, ref) => ( 10 | 15 | 16 | {children} 17 | 18 | 19 | 20 | 21 | )); 22 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; 23 | 24 | const ScrollBar = React.forwardRef< 25 | React.ElementRef, 26 | React.ComponentPropsWithoutRef 27 | >(({ className, orientation = 'vertical', ...props }, ref) => ( 28 | 41 | 42 | 43 | )); 44 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; 45 | 46 | export { ScrollArea, ScrollBar }; 47 | -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Separator = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >( 10 | ( 11 | { className, orientation = "horizontal", decorative = true, ...props }, 12 | ref 13 | ) => ( 14 | 25 | ) 26 | ) 27 | Separator.displayName = SeparatorPrimitive.Root.displayName 28 | 29 | export { Separator } 30 | -------------------------------------------------------------------------------- /src/components/ui/slider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SliderPrimitive from "@radix-ui/react-slider" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Slider = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, ...props }, ref) => ( 10 | 18 | 19 | 20 | 21 | 22 | 23 | )) 24 | Slider.displayName = SliderPrimitive.Root.displayName 25 | 26 | export { Slider } 27 | -------------------------------------------------------------------------------- /src/components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "next-themes" 2 | import { Toaster as Sonner } from "sonner" 3 | 4 | type ToasterProps = React.ComponentProps 5 | 6 | const Toaster = ({ ...props }: ToasterProps) => { 7 | const { theme = "system" } = useTheme() 8 | 9 | return ( 10 | 26 | ) 27 | } 28 | 29 | export { Toaster } 30 | -------------------------------------------------------------------------------- /src/components/ui/switch.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SwitchPrimitives from "@radix-ui/react-switch" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Switch = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, ...props }, ref) => ( 10 | 18 | 23 | 24 | )) 25 | Switch.displayName = SwitchPrimitives.Root.displayName 26 | 27 | export { Switch } 28 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |