├── .editorconfig ├── .env.sentry-build-plugin ├── .erb ├── configs │ ├── .eslintrc │ ├── webpack.config.base.ts │ ├── webpack.config.eslint.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 ├── 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 │ ├── install-chalk.js │ ├── link-modules.ts │ └── notarize.js ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── 1-Bug_report.md │ ├── 2-Question.md │ └── 3-Feature_request.md ├── compare_versions.py ├── config.yml ├── stale.yml └── workflows │ ├── lint.yml │ ├── package.yml │ ├── release.yml │ ├── test.yml │ └── tsc.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .vscode ├── extensions.json └── toreview.settings.json ├── LICENSE ├── README.md ├── assets ├── assets.d.ts ├── entitlements.mac.plist ├── icon.png └── webview-preload.js ├── cspell.json ├── images ├── desktop.PNG ├── desktop1.PNG ├── desktop2.PNG ├── desktop3.PNG ├── desktop4.PNG ├── logo.PNG └── minimap.png ├── package-lock.json ├── package.json ├── release └── app │ ├── package-lock.json │ └── package.json ├── snapcraft.yaml ├── src ├── __tests__ │ ├── About.test.tsx │ ├── AppMenu.test.tsx │ ├── BrowserInputSuggestions.test.tsx │ ├── ButtonAddBrowser.test.tsx │ ├── CertificateErrorPage.test.tsx │ ├── CloseButton.test.tsx │ ├── Documentation.test.tsx │ ├── Downloads.test.tsx │ ├── DownloadsPreview.test.tsx │ ├── Extensions.test.tsx │ ├── FIXME.Addaps.test.tsx │ ├── FIXME.App.test.tsx │ ├── FIXME.Board.test.tsx │ ├── FIXME.Bookmarks.test.tsx │ ├── FIXME.Browser.test.tsx │ ├── FIXME.BrowserControlBar.test.tsx │ ├── FIXME.BrowserTopBar.test.tsx │ ├── FIXME.History.test.tsx │ ├── FIXME.LeftBar.test.tsx │ ├── FIXME.Minimap.test.tsx │ ├── FIXME.SearchForm.test.tsx │ ├── FIXME.Settings.test.tsx │ ├── Loader.test.tsx │ ├── Popup.test.tsx │ ├── __snapshots__ │ │ ├── About.test.tsx.snap │ │ ├── Addaps.test.tsx.snap │ │ ├── App.test.tsx.snap │ │ ├── AppMenu.test.tsx.snap │ │ ├── Board.test.tsx.snap │ │ ├── Bookmarks.test.tsx.snap │ │ ├── Browser.test.tsx.snap │ │ ├── BrowserControlBar.test.tsx.snap │ │ ├── BrowserInputSuggestions.test.tsx.snap │ │ ├── BrowserTopBar.test.tsx.snap │ │ ├── ButtonAddBrowser.test.tsx.snap │ │ ├── CertificateErrorPage.test.tsx.snap │ │ ├── CloseButton.test.tsx.snap │ │ ├── Documentation.test.tsx.snap │ │ ├── Downloads.test.tsx.snap │ │ ├── DownloadsPreview.test.tsx.snap │ │ ├── Extensions.test.tsx.snap │ │ ├── FIXME.LeftBar.test.tsx.snap │ │ ├── FIXME.Minimap.test.tsx.snap │ │ ├── History.test.tsx.snap │ │ ├── Loader.test.tsx.snap │ │ ├── Popup.test.tsx.snap │ │ ├── SearchForm.test.tsx.snap │ │ └── Settings.test.tsx.snap │ └── beforeAll.tsx ├── main │ ├── analytics │ │ ├── firebase.ts │ │ └── tracker.ts │ ├── appEvents.ts │ ├── association.ts │ ├── bookmarks.ts │ ├── browser.ts │ ├── constants.ts │ ├── contextMenu.ts │ ├── db.ts │ ├── downloads.ts │ ├── extensions.ts │ ├── history.ts │ ├── i18n.ts │ ├── ipcMainEvents.ts │ ├── locales │ │ ├── ar.json │ │ ├── bn.json │ │ ├── cn.json │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fa.json │ │ ├── fr.json │ │ ├── hi.json │ │ ├── ja.json │ │ ├── nl.json │ │ ├── pl.json │ │ ├── pt.json │ │ ├── ru.json │ │ └── tr.json │ ├── logger.ts │ ├── main.js │ ├── main.ts │ ├── modules.d.ts │ ├── preload │ │ ├── app.ts │ │ └── titleBar.ts │ ├── store.ts │ ├── tabs.ts │ └── util.ts ├── namespaces │ └── _electronist.ts ├── renderer │ ├── App │ │ ├── App.css │ │ ├── App.tsx │ │ ├── components │ │ │ ├── About │ │ │ │ ├── About.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Addaps │ │ │ │ ├── Addaps.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ │ ├── AppMenu │ │ │ │ ├── AppMenu.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Board │ │ │ │ ├── Board.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Boards │ │ │ │ ├── Boards.tsx │ │ │ │ ├── BoardsItem │ │ │ │ │ ├── BoardsItem.tsx │ │ │ │ │ ├── Types.d.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Bookmarks │ │ │ │ ├── Bookmarks.tsx │ │ │ │ ├── BookmarksItem │ │ │ │ │ ├── BookmarksItem.tsx │ │ │ │ │ ├── Types.d.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── Import │ │ │ │ │ ├── Import.tsx │ │ │ │ │ ├── Types.d.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── style.scss │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Browser │ │ │ │ ├── Browser.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── BrowserControlBar │ │ │ │ ├── BrowserControlBar.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── BrowserInputSuggestions │ │ │ │ ├── BrowserInputSuggestions.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── BrowserTopBar │ │ │ │ ├── BrowserTopBar.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── ButtonAddBrowser │ │ │ │ ├── ButtonAddBrowser.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.css │ │ │ ├── CertificateErrorPage │ │ │ │ ├── CertificateErrorPage.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Chat │ │ │ │ ├── Chat.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── CloseButton │ │ │ │ ├── CloseButton.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Documentation │ │ │ │ ├── Documentation.tsx │ │ │ │ ├── KeyboardShortcuts │ │ │ │ │ ├── KeyboardShortcuts.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Types.d.ts │ │ │ │ ├── WebpagesWindows │ │ │ │ │ ├── WebpagesWindows.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Downloads │ │ │ │ ├── Downloads.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── DownloadsPreview │ │ │ │ ├── DownloadsPreview.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── ErrorFallback │ │ │ │ ├── ErrorFallback.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Extensions │ │ │ │ ├── Extensions.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── History │ │ │ │ ├── History.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── LeftBar │ │ │ │ ├── LeftBar.tsx │ │ │ │ ├── assets.ts │ │ │ │ ├── icon.png │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Loader │ │ │ │ ├── Loader.tsx │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── MacOSControls │ │ │ │ ├── MacOSControls.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Minimap │ │ │ │ ├── Minimap.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Notification │ │ │ │ ├── Notification.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── Ping │ │ │ │ ├── Ping.tsx │ │ │ │ └── index.tsx │ │ │ ├── PopoverColorPicker │ │ │ │ ├── PopoverColorPicker.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── styles.scss │ │ │ ├── Popup │ │ │ │ ├── Popup.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ ├── SearchForm │ │ │ │ ├── SearchForm.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── Settings │ │ │ │ ├── ApplicationSettings │ │ │ │ │ ├── ApplicationSettings.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── styles.scss │ │ │ │ ├── BrowsingSettings │ │ │ │ │ ├── BrowsingSettings.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── style.scss │ │ │ │ ├── ExtensionsSettings │ │ │ │ │ ├── ExtensionsSettings.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Settings.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.ts │ │ │ │ └── style.scss │ │ │ ├── SettingsStateSynced │ │ │ │ ├── SettingsStateSynced.tsx │ │ │ │ └── index.tsx │ │ │ └── Unmaximize │ │ │ │ ├── Unmaximize.tsx │ │ │ │ └── index.ts │ │ ├── firebase.tsx │ │ ├── helpers │ │ │ ├── d2.ts │ │ │ ├── dom.ts │ │ │ └── web.ts │ │ ├── hooks │ │ │ ├── useAnalytics.tsx │ │ │ ├── useBoard.tsx │ │ │ ├── useBrowserEvents.tsx │ │ │ ├── useBrowserMethods.tsx │ │ │ ├── useClickOutside.tsx │ │ │ ├── useGlobalEvents.tsx │ │ │ ├── useMessaging.tsx │ │ │ ├── useSettings.tsx │ │ │ └── useStoreHelpers.tsx │ │ ├── i18n-resources.d.ts │ │ ├── i18n.ts │ │ ├── index.tsx │ │ ├── locales │ │ │ ├── ar.json │ │ │ ├── bn.json │ │ │ ├── cn.json │ │ │ ├── de.json │ │ │ ├── en.json │ │ │ ├── es.json │ │ │ ├── fa.json │ │ │ ├── fr.json │ │ │ ├── hi.json │ │ │ ├── ja.json │ │ │ ├── nl.json │ │ │ ├── pl.json │ │ │ ├── pt.json │ │ │ ├── ru.json │ │ │ └── tr.json │ │ ├── preload.d.ts │ │ ├── store │ │ │ ├── hooks.ts │ │ │ ├── migrations.tsx │ │ │ ├── reducers │ │ │ │ ├── Board.ts │ │ │ │ ├── Downloads.ts │ │ │ │ └── Settings.ts │ │ │ └── store.ts │ │ ├── style │ │ │ ├── dark.css │ │ │ └── light.css │ │ └── svg │ │ │ └── loading.svg │ ├── TitleBar │ │ ├── TitleBar.scss │ │ ├── TitleBar.tsx │ │ ├── components │ │ │ ├── AppControls │ │ │ │ ├── AppControls.tsx │ │ │ │ ├── Unmaximize │ │ │ │ │ ├── Unmaximize.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ │ └── TopBar │ │ │ │ ├── TopBar.tsx │ │ │ │ ├── Types.d.ts │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ ├── index.tsx │ │ ├── preload.d.ts │ │ ├── store │ │ │ ├── hooks.ts │ │ │ ├── reducers │ │ │ │ └── Tabs.ts │ │ │ └── store.ts │ │ └── style │ │ │ ├── dark.css │ │ │ └── light.css │ └── index.ejs └── types │ ├── analytics.ts │ ├── boards.ts │ ├── bookmarks.ts │ ├── configKeys.ts │ ├── downloads.ts │ ├── extensions.ts │ ├── history.ts │ ├── i18n.ts │ ├── ipc.ts │ ├── messaging.ts │ └── suggestions.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.env.sentry-build-plugin: -------------------------------------------------------------------------------- 1 | # DO NOT commit this file to your repository! 2 | # The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin. 3 | # It's used for authentication when uploading source maps. 4 | # You can also set this env variable in your own `.env` files and remove this file. 5 | SENTRY_AUTH_TOKEN=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 6 | -------------------------------------------------------------------------------- /.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 webpack from 'webpack'; 6 | import webpackPaths from './webpack.paths'; 7 | import { dependencies as externals } from '../../release/app/package.json'; 8 | 9 | const configuration: webpack.Configuration = { 10 | externals: [...Object.keys(externals || {})], 11 | 12 | stats: 'errors-only', 13 | 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.[jt]sx?$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'ts-loader', 21 | options: { 22 | // Remove this line to enable type checking in webpack builds 23 | transpileOnly: true, 24 | }, 25 | }, 26 | }, 27 | ], 28 | }, 29 | 30 | output: { 31 | path: webpackPaths.srcPath, 32 | // https://github.com/webpack/webpack/issues/1114 33 | library: { 34 | type: 'commonjs2', 35 | }, 36 | }, 37 | 38 | /** 39 | * Determine the array of extensions that should be used to resolve modules. 40 | */ 41 | resolve: { 42 | extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], 43 | modules: [webpackPaths.srcPath, 'node_modules'], 44 | // fallback: { fs: false, path: false }, 45 | }, 46 | 47 | plugins: [ 48 | new webpack.EnvironmentPlugin({ 49 | NODE_ENV: 'production', 50 | }), 51 | ], 52 | }; 53 | 54 | export default configuration; 55 | -------------------------------------------------------------------------------- /.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.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 devtoolsConfig = 19 | process.env.DEBUG_PROD === 'true' 20 | ? { 21 | devtool: 'source-map', 22 | } 23 | : {}; 24 | 25 | const configuration: webpack.Configuration = { 26 | ...devtoolsConfig, 27 | 28 | mode: 'production', 29 | 30 | target: 'electron-main', 31 | 32 | entry: { 33 | main: path.join(webpackPaths.srcMainPath, 'main.ts'), 34 | appPreload: path.join(webpackPaths.srcMainPath, 'preload/app.ts'), 35 | titleBarPreload: path.join(webpackPaths.srcMainPath, 'preload/titleBar.ts'), 36 | }, 37 | 38 | output: { 39 | path: webpackPaths.distMainPath, 40 | filename: '[name].js', 41 | }, 42 | 43 | optimization: { 44 | minimizer: [ 45 | new TerserPlugin({ 46 | parallel: true, 47 | }), 48 | ], 49 | }, 50 | 51 | plugins: [ 52 | new BundleAnalyzerPlugin({ 53 | analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', 54 | }), 55 | 56 | /** 57 | * Create global constants which can be configured at compile time. 58 | * 59 | * Useful for allowing different behaviour between development builds and 60 | * release builds 61 | * 62 | * NODE_ENV should be production so that modules do not perform certain 63 | * development checks 64 | */ 65 | new webpack.EnvironmentPlugin({ 66 | NODE_ENV: 'production', 67 | DEBUG_PROD: 'false', 68 | START_MINIMIZED: 'false', 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: { 23 | app: path.join(webpackPaths.srcMainPath, 'preload/app.ts'), 24 | titleBar: path.join(webpackPaths.srcMainPath, 'preload/titleBar.ts'), 25 | }, 26 | 27 | output: { 28 | path: webpackPaths.dllPath, 29 | filename: '[name].preload.js', 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 dllPath = path.join(__dirname, '../dll'); 6 | 7 | const srcPath = path.join(rootPath, 'src'); 8 | const srcMainPath = path.join(srcPath, 'main'); 9 | const srcRendererPath = path.join(srcPath, 'renderer'); 10 | 11 | const releasePath = path.join(rootPath, 'release'); 12 | const appPath = path.join(releasePath, 'app'); 13 | const appPackagePath = path.join(appPath, 'package.json'); 14 | const appNodeModulesPath = path.join(appPath, 'node_modules'); 15 | const srcNodeModulesPath = path.join(srcPath, 'node_modules'); 16 | 17 | const distPath = path.join(appPath, 'dist'); 18 | const distMainPath = path.join(distPath, 'main'); 19 | const distRendererPath = path.join(distPath, 'renderer'); 20 | 21 | const buildPath = path.join(releasePath, 'build'); 22 | 23 | export default { 24 | rootPath, 25 | dllPath, 26 | srcPath, 27 | srcMainPath, 28 | srcRendererPath, 29 | releasePath, 30 | appPath, 31 | appPackagePath, 32 | appNodeModulesPath, 33 | srcNodeModulesPath, 34 | distPath, 35 | distMainPath, 36 | distRendererPath, 37 | buildPath, 38 | }; 39 | -------------------------------------------------------------------------------- /.erb/img/erb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/.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, 'app.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 rimraf from 'rimraf'; 2 | import process from 'process'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | const args = process.argv.slice(2); 6 | const commandMap = { 7 | dist: webpackPaths.distPath, 8 | release: webpackPaths.releasePath, 9 | dll: webpackPaths.dllPath, 10 | }; 11 | 12 | args.forEach((x) => { 13 | const pathToRemove = commandMap[x]; 14 | if (pathToRemove !== undefined) { 15 | rimraf.sync(pathToRemove); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /.erb/scripts/delete-source-maps.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import rimraf from 'rimraf'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | export default function deleteSourceMaps() { 6 | rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map')); 7 | rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map')); 8 | } 9 | -------------------------------------------------------------------------------- /.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/install-chalk.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const fs = require('fs'); 3 | 4 | // Check if chalk is already installed 5 | const isChalkInstalled = () => { 6 | try { 7 | require.resolve('chalk'); 8 | return true; 9 | } catch (e) { 10 | return false; 11 | } 12 | }; 13 | 14 | // Install chalk if not installed 15 | if (!isChalkInstalled()) { 16 | console.log('Installing chalk...'); 17 | execSync('npm install chalk', { stdio: 'inherit' }); 18 | } else { 19 | console.log('Chalk is already installed.'); 20 | } 21 | -------------------------------------------------------------------------------- /.erb/scripts/link-modules.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import webpackPaths from '../configs/webpack.paths'; 3 | 4 | const { srcNodeModulesPath } = webpackPaths; 5 | const { appNodeModulesPath } = webpackPaths; 6 | 7 | if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) { 8 | fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); 9 | } 10 | -------------------------------------------------------------------------------- /.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 (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) { 16 | console.warn( 17 | 'Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set' 18 | ); 19 | return; 20 | } 21 | 22 | const appName = context.packager.appInfo.productFilename; 23 | 24 | await notarize({ 25 | appBundleId: build.appId, 26 | appPath: `${appOutDir}/${appName}.app`, 27 | appleId: process.env.APPLE_ID, 28 | appleIdPassword: process.env.APPLE_ID_PASS, 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /.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 | !.erb 34 | 35 | *.d.ts 36 | 37 | assets 38 | 39 | src/main/main.js -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'erb', 3 | rules: { 4 | // A temporary hack related to IDE not resolving correct package.json 5 | 'import/no-extraneous-dependencies': 'off', 6 | 'import/no-unresolved': 'error', 7 | // Since React 17 and typescript 4.1 you can safely disable the rule 8 | 'react/react-in-jsx-scope': 'off', 9 | 'import/extensions': 'off', 10 | 'react/jsx-filename-extension': 'off', 11 | 'no-unused-vars': 'off', 12 | 'no-console': 'off', 13 | 'no-unused-expressions': 'off', 14 | 'no-restricted-exports': 'off', 15 | 'no-loop-func': 'off', 16 | 'react/function-component-definition': 'off', 17 | 'react/no-unused-prop-types': 'off', 18 | 'prettier/prettier': 'off', 19 | 'promise/always-return': 'off', 20 | 'import/prefer-default-export': 'off', 21 | 'jsx-a11y/click-events-have-key-events': 'off', 22 | 'jsx-a11y/no-static-element-interactions': 'off', 23 | 'react/jsx-props-no-spreading': 'off', 24 | }, 25 | parserOptions: { 26 | ecmaVersion: 2020, 27 | sourceType: 'module', 28 | project: './tsconfig.json', 29 | tsconfigRootDir: __dirname, 30 | createDefaultProgram: true, 31 | }, 32 | settings: { 33 | 'import/resolver': { 34 | // See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below 35 | node: {}, 36 | webpack: { 37 | config: require.resolve('./.erb/configs/webpack.config.eslint.ts'), 38 | }, 39 | typescript: {}, 40 | }, 41 | 'import/parsers': { 42 | '@typescript-eslint/parser': ['.ts', '.tsx'], 43 | }, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | **Version:** 8 | **Operating system:** 9 | 10 | 11 | 12 | ## Describe abnormal behavior 13 | 14 | 15 | 16 | 17 | ## Steps to reproduce 18 | 19 | 20 | 21 | 1. 22 | 23 | ## Screenshots and screencasts 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-Question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question.❓ 4 | labels: 'question' 5 | --- 6 | 7 | ## Summary 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: You want something added to BonBon. 🎉 4 | labels: 'enhancement' 5 | --- 6 | -------------------------------------------------------------------------------- /.github/compare_versions.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import semver 4 | import re 5 | 6 | def extract_version(tag): 7 | # Extracts a valid SemVer string from the given tag 8 | match = re.search(r'v(\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?)', tag) 9 | return match.group(1) if match else None 10 | 11 | # Get the tag from the environment 12 | tag = os.getenv("TAG", "") 13 | tag_simplified = extract_version(tag) 14 | 15 | # Load versions from package.json files 16 | with open("./package.json", "r") as f: 17 | main_package_json = json.load(f) 18 | main_package_json_version = main_package_json["version"] 19 | 20 | with open("./release/app/package.json", "r") as f: 21 | release_package_json = json.load(f) 22 | release_package_json_version = release_package_json["version"] 23 | 24 | # Check if the extracted tag version is valid 25 | if not tag_simplified: 26 | raise ValueError(f"{tag} is not a valid SemVer string") 27 | 28 | # Compare the versions 29 | are_package_jsons_equal = semver.compare(main_package_json_version, release_package_json_version) 30 | is_package_json_version_and_tag_equal = semver.compare(main_package_json_version, tag_simplified) 31 | 32 | # Output result based on comparisons 33 | if is_package_json_version_and_tag_equal == 0 and are_package_jsons_equal == 0: 34 | print("All good") 35 | else: 36 | print(f"Version comparison results: Tag vs Main: {is_package_json_version_and_tag_equal}, Main vs Release: {are_package_jsons_equal}") 37 | raise RuntimeError("Version mismatch detected.") 38 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | requiredHeaders: 2 | - Expected Behavior 3 | - Current Behavior 4 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 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/lint.yml: -------------------------------------------------------------------------------- 1 | name: ESLint 2 | 3 | on: push 4 | 5 | jobs: 6 | deps: 7 | name: Setup and get deps 8 | runs-on: ${{ matrix.os }} 9 | 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest] 13 | 14 | steps: 15 | - name: Check out Git repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Install Node.js and NPM 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 20 22 | cache: npm 23 | 24 | - name: Install dependencies 25 | run: npm ci --legacy-peer-deps 26 | 27 | - name: Run ESLint 28 | run: npm run lint 29 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: Package 2 | 3 | on: push 4 | 5 | jobs: 6 | deps: 7 | name: Setup, Test, and Package 8 | runs-on: ${{ matrix.os }} 9 | 10 | strategy: 11 | matrix: 12 | os: [macos-latest, windows-latest, ubuntu-latest] 13 | 14 | steps: 15 | - name: Check out Git repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Install Node.js and NPM 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 20 22 | cache: npm 23 | 24 | - name: install ts-node for everyone 25 | run: npm install -g ts-node husky 26 | 27 | - name: Install dependencies 28 | run: | 29 | npm ci --omit=dev --legacy-peer-deps 30 | 31 | - name: Compile 32 | run: | 33 | npm run build 34 | 35 | - name: Package 36 | env: 37 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | run: | 39 | npm exec electron-builder build --publish=never 40 | 41 | - if: matrix.os == 'ubuntu-latest' 42 | name: Ubuntu artifacts upload 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: BonBon-linux 46 | path: | 47 | release/build/*-latest.yml 48 | release/build/BonBon-*.*.*.snap 49 | release/build/BonBon-*.*.*.flatpak 50 | release/build/BonBon-*.*.*.tar.gz 51 | release/build/BonBon-*.*.*.AppImage 52 | release/build/BonBon-*.*.*.deb 53 | release/build/BonBon-*.*.*.rpm 54 | 55 | - if: matrix.os == 'macos-latest' 56 | name: MacOS artifacts upload 57 | uses: actions/upload-artifact@v4 58 | with: 59 | name: BonBon-macos 60 | path: | 61 | release/build/*-latest.yml 62 | release/build/BonBon-*.*.*-arm64.dmg 63 | release/build/BonBon-*.*.*.dmg 64 | 65 | - if: matrix.os == 'windows-latest' 66 | name: Windows artifacts upload 67 | uses: actions/upload-artifact@v4 68 | with: 69 | name: BonBon-windows 70 | path: | 71 | release/build/*-latest.yml 72 | release/build/BonBon Setup *.*.*.exe 73 | release/build/BonBon-*.*.*.exe 74 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | 9 | jobs: 10 | deps: 11 | name: Setup and get deps 12 | runs-on: ${{ matrix.os }} 13 | 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest] 17 | 18 | steps: 19 | - name: Check out Git repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Install Node.js and NPM 23 | uses: actions/setup-node@v4 24 | with: 25 | node-version: 20 26 | cache: npm 27 | 28 | - name: Install dependencies 29 | run: npm ci --legacy-peer-deps 30 | 31 | - name: Compile 32 | run: npm run build 33 | 34 | - name: Check tests 35 | run: npm run test 36 | -------------------------------------------------------------------------------- /.github/workflows/tsc.yml: -------------------------------------------------------------------------------- 1 | name: TSC 2 | 3 | on: push 4 | 5 | jobs: 6 | deps: 7 | name: Setup and get deps 8 | runs-on: ${{ matrix.os }} 9 | 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest] 13 | 14 | steps: 15 | - name: Check out Git repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Install Node.js and NPM 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 20 22 | cache: npm 23 | 24 | - name: Install dependencies 25 | run: npm ci --legacy-peer-deps 26 | 27 | - name: Check types 28 | run: npm exec tsc 29 | -------------------------------------------------------------------------------- /.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/node_modules 22 | release/app/dist 23 | release/build 24 | .erb/dll 25 | 26 | .idea 27 | npm-debug.log.* 28 | *.css.d.ts 29 | *.sass.d.ts 30 | *.scss.d.ts 31 | 32 | src/renderer/localazy.json 33 | src/main/localazy.json -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/.npmrc -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.15.1 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "streetsidesoftware.code-spell-checker"] 3 | } -------------------------------------------------------------------------------- /.vscode/toreview.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false, 3 | "editor.codeActionsOnSave": { 4 | "source.organizeImports": true // deletes unused imports on save 5 | }, 6 | "typescript.preferences.importModuleSpecifier": "non-relative", // references baseUrl in tsconfig 7 | } 8 | -------------------------------------------------------------------------------- /assets/assets.d.ts: -------------------------------------------------------------------------------- 1 | type Styles = Record; 2 | 3 | declare module '*.svg' { 4 | const content: string; 5 | export default content; 6 | } 7 | 8 | declare module '*.png' { 9 | const content: string; 10 | export default content; 11 | } 12 | 13 | declare module '*.jpg' { 14 | const content: string; 15 | export default content; 16 | } 17 | 18 | declare module '*.scss' { 19 | const content: Styles; 20 | export default content; 21 | } 22 | 23 | declare module '*.sass' { 24 | const content: Styles; 25 | export default content; 26 | } 27 | 28 | declare module '*.css' { 29 | const content: Styles; 30 | export default content; 31 | } 32 | -------------------------------------------------------------------------------- /assets/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/assets/icon.png -------------------------------------------------------------------------------- /images/desktop.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/desktop.PNG -------------------------------------------------------------------------------- /images/desktop1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/desktop1.PNG -------------------------------------------------------------------------------- /images/desktop2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/desktop2.PNG -------------------------------------------------------------------------------- /images/desktop3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/desktop3.PNG -------------------------------------------------------------------------------- /images/desktop4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/desktop4.PNG -------------------------------------------------------------------------------- /images/logo.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/logo.PNG -------------------------------------------------------------------------------- /images/minimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/images/minimap.png -------------------------------------------------------------------------------- /release/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BonBon", 3 | "version": "1.0.7", 4 | "description": "BonBon Browser", 5 | "license": "GPL-3.0", 6 | "author": { 7 | "name": "Daniel Febrero", 8 | "email": "febrero.daniel@gmail.com", 9 | "url": "https://github.com/BonBon-exchange/bonbon-browser" 10 | }, 11 | "main": "./dist/main/main.js", 12 | "scripts": { 13 | "electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js", 14 | "postinstall": "npm run electron-rebuild && npm run link-modules", 15 | "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts" 16 | }, 17 | "dependencies": { 18 | "sqlite3": "^5.1.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: bonbon-browser 2 | version: '1.0.6' 3 | summary: A web browser built with Electron, TypeScript, and React 4 | description: | 5 | BonBon Web Browser is an open-source web browser built using Electron, React, and TypeScript. It provides a customizable and easy-to-use interface with modern web technologies. 6 | 7 | grade: stable 8 | confinement: strict 9 | 10 | apps: 11 | bonbon: 12 | command: desktop-launch $SNAP/bonbon-browser/bonbon 13 | plugs: 14 | - network 15 | - browser-support 16 | - network-bind 17 | - pulseaudio 18 | - opengl 19 | - home 20 | - x11 21 | - unity7 22 | - wayland 23 | 24 | parts: 25 | bonbon-browser: 26 | plugin: dump 27 | source: . 28 | stage-packages: 29 | - libnss3 30 | - libgtk-3-0 31 | - libxss1 32 | - libgconf-2-4 33 | build-packages: 34 | - nodejs 35 | - npm 36 | override-build: | 37 | npm install 38 | npm run build 39 | npm run package 40 | after: 41 | - desktop-gtk3 42 | -------------------------------------------------------------------------------- /src/__tests__/About.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | /* eslint-disable react/no-deprecated */ 3 | import '@testing-library/jest-dom'; 4 | import { act } from 'react'; 5 | import { render } from 'react-dom'; 6 | import { Provider } from 'react-redux'; 7 | import configureStore from 'redux-mock-store'; 8 | import { Middleware } from '@reduxjs/toolkit'; 9 | import pretty from 'pretty'; 10 | 11 | import { About } from '../renderer/App/components/About'; 12 | import { mockWindow } from './beforeAll'; 13 | import { initialState } from '../renderer/App/store/reducers/Board'; 14 | import packagejson from '../../package.json'; 15 | 16 | let store: any; 17 | let container: any; 18 | const middlewares: Middleware[] = []; 19 | 20 | describe('About', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | beforeEach(() => { 28 | // setup a DOM element as a render target 29 | container = document.createElement('div'); 30 | document.body.appendChild(container); 31 | }); 32 | 33 | it('should show app version and match snapshot', () => { 34 | act(() => { 35 | render( 36 | 37 | 38 | , 39 | container 40 | ); 41 | }); 42 | 43 | expect(pretty(container.innerHTML)).toMatchSnapshot(); 44 | expect(container.innerHTML.indexOf(packagejson.version) > 1).toBeTruthy(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/__tests__/AppMenu.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { AppMenu } from '../renderer/App/components/AppMenu'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let store: any; 13 | let tree: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | showAbout: jest.fn(), 18 | showSettings: jest.fn(), 19 | showBookmarks: jest.fn(), 20 | showHistory: jest.fn(), 21 | showDownloads: jest.fn(), 22 | showDocumentation: jest.fn(), 23 | showExtensions: jest.fn(), 24 | showBoards: jest.fn(), 25 | }; 26 | 27 | describe('AppMenu', () => { 28 | beforeAll(() => { 29 | mockWindow(); 30 | const mockStore = configureStore(middlewares); 31 | store = mockStore({ board: initialState }); 32 | }); 33 | 34 | it('should render', () => { 35 | act(() => { 36 | tree = renderer.create( 37 | 38 | 39 | 40 | ); 41 | }); 42 | 43 | expect(tree).toMatchSnapshot(); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/__tests__/BrowserInputSuggestions.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { BrowserInputSuggestions } from '../renderer/App/components/BrowserInputSuggestions'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | inputValue: 'dani', 18 | handleSuggestionClick: jest.fn(), 19 | setSelectedSuggestion: jest.fn(), 20 | setDomainSuggestionResults: jest.fn(), 21 | }; 22 | 23 | describe('BrowserInputSuggestions', () => { 24 | beforeAll(() => { 25 | mockWindow(); 26 | const mockStore = configureStore(middlewares); 27 | store = mockStore({ board: initialState }); 28 | }); 29 | 30 | it('should render', () => { 31 | act(() => { 32 | tree = renderer.create( 33 | 34 | 35 | 36 | ); 37 | }); 38 | 39 | expect(tree).toMatchSnapshot(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/__tests__/ButtonAddBrowser.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { ButtonAddBrowser } from '../renderer/App/components/ButtonAddBrowser'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const buttonAddBrowserProps = { 17 | onClick: jest.fn(), 18 | }; 19 | 20 | describe('ButtonAddBrowser', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | it('should render', () => { 28 | act(() => { 29 | tree = renderer.create( 30 | 31 | 32 | 33 | ); 34 | }); 35 | 36 | expect(tree).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/__tests__/CertificateErrorPage.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { CertificateErrorPage } from '../renderer/App/components/CertificateErrorPage'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | webContentsId: 10, 18 | browserId: '1id', 19 | fingerprint: 'finger', 20 | reload: jest.fn(), 21 | }; 22 | 23 | describe('CertificateErrorPage', () => { 24 | beforeAll(() => { 25 | mockWindow(); 26 | const mockStore = configureStore(middlewares); 27 | store = mockStore({ board: initialState }); 28 | }); 29 | 30 | it('should render', () => { 31 | act(() => { 32 | tree = renderer.create( 33 | 34 | 35 | 36 | ); 37 | }); 38 | 39 | expect(tree).toMatchSnapshot(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/__tests__/CloseButton.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { CloseButton } from '../renderer/App/components/CloseButton'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | handleClose: jest.fn(), 18 | }; 19 | 20 | describe('CloseButton', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | it('should render', () => { 28 | act(() => { 29 | tree = renderer.create( 30 | 31 | 32 | 33 | ); 34 | }); 35 | 36 | expect(tree).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/__tests__/Documentation.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { render, fireEvent, screen } from '@testing-library/react'; 4 | import { Provider } from 'react-redux'; 5 | import configureStore from 'redux-mock-store'; 6 | import { Middleware } from '@reduxjs/toolkit'; 7 | import renderer, { act } from 'react-test-renderer'; 8 | import pretty from 'pretty'; 9 | 10 | import { Documentation } from '../renderer/App/components/Documentation'; 11 | import { mockWindow } from './beforeAll'; 12 | import { initialState } from '../renderer/App/store/reducers/Board'; 13 | 14 | let tree: any; 15 | let store: any; 16 | let container: any; 17 | const middlewares: Middleware[] = []; 18 | 19 | const props = { 20 | handleClose: jest.fn(), 21 | }; 22 | 23 | describe('Documentation', () => { 24 | beforeAll(() => { 25 | mockWindow(); 26 | const mockStore = configureStore(middlewares); 27 | store = mockStore({ board: initialState }); 28 | }); 29 | 30 | beforeEach(() => { 31 | container = null; 32 | }); 33 | 34 | it('should render', () => { 35 | act(() => { 36 | tree = renderer.create( 37 | 38 | 39 | 40 | ); 41 | }); 42 | 43 | expect(tree).toMatchSnapshot(); 44 | }); 45 | 46 | it('should show Webpages windows', async () => { 47 | act(() => { 48 | const renderered = render( 49 | 50 | 51 | 52 | ); 53 | container = renderered.container; 54 | }); 55 | 56 | act(() => { 57 | fireEvent.click(screen.getAllByTestId('documentation-webpages-link')[0]); 58 | }); 59 | 60 | expect( 61 | screen.getAllByTestId('documentation-webpages-page')[0] 62 | ).toBeTruthy(); 63 | 64 | expect(pretty(container.innerHTML)).toMatchSnapshot(); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /src/__tests__/Downloads.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { Downloads } from '../renderer/App/components/Downloads'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | handleClose: jest.fn(), 18 | }; 19 | 20 | describe('Downloads', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | it('should render', () => { 28 | act(() => { 29 | tree = renderer.create( 30 | 31 | 32 | 33 | ); 34 | }); 35 | 36 | expect(tree).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/__tests__/DownloadsPreview.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { DownloadsPreview } from '../renderer/App/components/DownloadsPreview'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Downloads'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | describe('DownloadsPreview', () => { 17 | beforeAll(() => { 18 | mockWindow(); 19 | const mockStore = configureStore(middlewares); 20 | store = mockStore({ downloads: initialState }); 21 | }); 22 | 23 | it('should render', () => { 24 | act(() => { 25 | tree = renderer.create( 26 | 27 | 28 | 29 | ); 30 | }); 31 | 32 | expect(tree).toMatchSnapshot(); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/__tests__/Extensions.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { Extensions } from '../renderer/App/components/Extensions'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | handleClose: jest.fn(), 18 | }; 19 | 20 | describe('Extensions', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | it('should render', () => { 28 | act(() => { 29 | tree = renderer.create( 30 | 31 | 32 | 33 | ); 34 | }); 35 | 36 | expect(tree).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/__tests__/FIXME.App.test.tsx: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import { act, render } from '@testing-library/react'; 3 | import renderer from 'react-test-renderer'; 4 | import pretty from 'pretty'; 5 | 6 | import { mockWindow } from './beforeAll'; 7 | import { App } from '../renderer/App/App'; 8 | 9 | let tree: any; 10 | let container: any; 11 | 12 | describe('App', () => { 13 | beforeAll(() => { 14 | mockWindow(); 15 | }); 16 | 17 | beforeEach(() => { 18 | tree = null; 19 | // setup a DOM element as a render target 20 | container = document.createElement('div'); 21 | document.body.appendChild(container); 22 | }); 23 | 24 | it('should render and match snapshot', () => { 25 | act(() => { 26 | tree = renderer.create(); 27 | }); 28 | expect(tree).toMatchSnapshot(); 29 | }); 30 | 31 | it('should load a board and match snapshot', () => { 32 | return new Promise((resolve) => { 33 | act(() => { 34 | const appRenderer = render(); 35 | container = appRenderer.container; 36 | }); 37 | 38 | setTimeout(() => { 39 | act(() => { 40 | const ev = new Event('load-board'); 41 | window.dispatchEvent(ev); 42 | 43 | setTimeout(() => { 44 | expect(pretty(container.innerHTML)).toMatchSnapshot(); 45 | resolve(true); 46 | }, 5000); 47 | }); 48 | }, 5000); 49 | }); 50 | }, 20000); 51 | }); 52 | -------------------------------------------------------------------------------- /src/__tests__/FIXME.BrowserControlBar.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { BrowserControlBar } from '../renderer/App/components/BrowserControlBar'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const browserControlBarProps = { 17 | url: 'https://www.github.com', 18 | browserId: '123abc', 19 | }; 20 | 21 | describe('BrowserControlBar', () => { 22 | beforeAll(() => { 23 | mockWindow(); 24 | const mockStore = configureStore(middlewares); 25 | store = mockStore({ board: initialState }); 26 | }); 27 | 28 | it('should render', () => { 29 | act(() => { 30 | tree = renderer.create( 31 | 32 | 33 | 34 | ); 35 | }); 36 | 37 | expect(tree).toMatchSnapshot(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/__tests__/FIXME.BrowserTopBar.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import { Middleware } from '@reduxjs/toolkit'; 3 | import '@testing-library/jest-dom'; 4 | import { Provider } from 'react-redux'; 5 | import configureStore from 'redux-mock-store'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { BrowserTopBar } from '../renderer/App/components/BrowserTopBar'; 9 | import { initialState } from '../renderer/App/store/reducers/Board'; 10 | import { mockWindow } from './beforeAll'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const browserTopBarProps = { 17 | closeBrowser: jest.fn(), 18 | toggleFullSizeBrowser: jest.fn(), 19 | title: 'Github', 20 | onClick: jest.fn(), 21 | minimizeBrowser: jest.fn(), 22 | isMaximized: true, 23 | isLoading: false, 24 | isPinned: false, 25 | }; 26 | 27 | describe('BrowserTopBar', () => { 28 | beforeAll(() => { 29 | mockWindow(); 30 | const mockStore = configureStore(middlewares); 31 | store = mockStore({ board: initialState }); 32 | }); 33 | 34 | it('should render', () => { 35 | act(() => { 36 | tree = renderer.create( 37 | 38 | 39 | 40 | ); 41 | }); 42 | 43 | expect(tree).toMatchSnapshot(); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/__tests__/FIXME.History.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { History } from '../renderer/App/components/History'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | handleClose: jest.fn(), 18 | }; 19 | 20 | describe('History', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | it('should render', () => { 28 | act(() => { 29 | tree = renderer.create( 30 | 31 | 32 | 33 | ); 34 | }); 35 | 36 | expect(tree).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/__tests__/FIXME.SearchForm.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { SearchForm } from '../renderer/App/components/SearchForm'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const props = { 17 | browserId: '10id', 18 | }; 19 | 20 | describe('SearchForm', () => { 21 | beforeAll(() => { 22 | mockWindow(); 23 | const mockStore = configureStore(middlewares); 24 | store = mockStore({ board: initialState }); 25 | }); 26 | 27 | it('should render', () => { 28 | act(() => { 29 | tree = renderer.create( 30 | 31 | 32 | 33 | ); 34 | }); 35 | 36 | expect(tree).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/__tests__/Loader.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { Loader } from '../renderer/App/components/Loader'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | describe('Loader', () => { 17 | beforeAll(() => { 18 | mockWindow(); 19 | const mockStore = configureStore(middlewares); 20 | store = mockStore({ board: initialState }); 21 | }); 22 | 23 | it('should render', () => { 24 | act(() => { 25 | tree = renderer.create( 26 | 27 | 28 | 29 | ); 30 | }); 31 | 32 | expect(tree).toMatchSnapshot(); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/__tests__/Popup.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import '@testing-library/jest-dom'; 3 | import { Provider } from 'react-redux'; 4 | import configureStore from 'redux-mock-store'; 5 | import { Middleware } from '@reduxjs/toolkit'; 6 | import renderer, { act } from 'react-test-renderer'; 7 | 8 | import { Popup } from '../renderer/App/components/Popup'; 9 | import { mockWindow } from './beforeAll'; 10 | import { initialState } from '../renderer/App/store/reducers/Board'; 11 | 12 | let tree: any; 13 | let store: any; 14 | const middlewares: Middleware[] = []; 15 | 16 | const popupProps = { 17 | closePopup: jest.fn(), 18 | title: 'You', 19 | }; 20 | 21 | describe('Library', () => { 22 | beforeAll(() => { 23 | mockWindow(); 24 | const mockStore = configureStore(middlewares); 25 | store = mockStore({ board: initialState }); 26 | }); 27 | 28 | it('should render', () => { 29 | act(() => { 30 | tree = renderer.create( 31 | 32 | 33 | Hello 34 | 35 | 36 | ); 37 | }); 38 | 39 | expect(tree).toMatchSnapshot(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/About.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`About should show app version and match snapshot 1`] = ` 4 | "
App version: 1.0.5
5 |
Author: Daniel Febrero
" 6 | `; 7 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/AppMenu.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`AppMenu should render 1`] = ` 4 |
9 |
    10 |
  • 13 | Toggle dark mode 14 |
  • 15 |
  • 18 | Boards 19 |
  • 20 |
  • 23 | Extensions 24 |
  • 25 |
  • 28 | Downloads 29 |
  • 30 |
  • 33 | Bookmarks 34 |
  • 35 |
  • 38 | History 39 |
  • 40 |
  • 43 | Settings 44 |
  • 45 |
  • 48 | Documentation 49 |
  • 50 |
  • 53 | About 54 |
  • 55 |
56 |
57 | `; 58 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/BrowserInputSuggestions.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`BrowserInputSuggestions should render 1`] = ` 4 |
7 |
    8 |
9 | `; 10 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/BrowserTopBar.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`BrowserTopBar should render 1`] = ` 4 |
9 |
12 | Github 13 |
14 |
17 |
22 | 29 | 32 | 33 |
34 |
39 | 46 | 49 | 54 | 55 |
56 |
61 | 68 | 71 | 72 |
73 |
74 |
75 | `; 76 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/ButtonAddBrowser.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ButtonAddBrowser should render 1`] = ` 4 | 13 | 16 | 17 | `; 18 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/CertificateErrorPage.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CertificateErrorPage should render 1`] = ` 4 |
7 |
10 | The certificate for this page is incorrect. Visiting the website could be dangerous. 11 |
12 |
15 | 39 |
40 |
41 | `; 42 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/CloseButton.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CloseButton should render 1`] = ` 4 |
9 | 16 | 19 | 20 |
21 | `; 22 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/Downloads.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Downloads should render 1`] = ` 4 |
7 |
12 | 19 | 22 | 23 |
24 |
27 |

28 | Downloads 29 |

30 | 54 | 60 |
61 |
62 | `; 63 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/DownloadsPreview.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`DownloadsPreview should render 1`] = ` 4 |
7 |
    8 |
    12 | 35 |
    36 |
37 | `; 38 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/Extensions.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Extensions should render 1`] = ` 4 |
7 |
12 | 19 | 22 | 23 |
24 |
27 |

28 | Extensions 29 |

30 | 36 |
37 |
38 | `; 39 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/FIXME.LeftBar.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`LeftBar should render 1`] = ` 4 |
7 |
    10 |
    13 | 22 | 25 | 26 |
27 |
28 | `; 29 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/FIXME.Minimap.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Minimap should render 1`] = ` 4 |
7 |
16 |
17 | `; 18 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/History.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`History should render 1`] = ` 4 |
7 |
12 | 19 | 22 | 23 |
24 |
27 |

28 | History 29 |

30 | 54 | 60 |
63 | 66 |
67 |
68 |
69 | `; 70 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/Loader.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Loader should render 1`] = ` 4 |
7 | 10 |
11 | `; 12 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/Popup.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Library should render 1`] = ` 4 |
8 | 39 |
40 | `; 41 | -------------------------------------------------------------------------------- /src/main/analytics/firebase.ts: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from 'firebase/app'; 3 | import { getFirestore } from 'firebase/firestore'; 4 | import { getAuth, signInAnonymously } from 'firebase/auth'; 5 | 6 | // Your web app's Firebase configuration 7 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 8 | const firebaseConfig = { 9 | apiKey: 'AIzaSyBZ0V6V43EW3NHE-z0fZZnlpe22mE55uqQ', 10 | authDomain: 'bonbon-browser.firebaseapp.com', 11 | databaseURL: 12 | 'https://bonbon-browser-default-rtdb.europe-west1.firebasedatabase.app', 13 | projectId: 'bonbon-browser', 14 | storageBucket: 'bonbon-browser.appspot.com', 15 | messagingSenderId: '313574192651', 16 | appId: '1:313574192651:web:cbe8cd4ce4797d7c82f90e', 17 | measurementId: 'G-GVME8GLMGZ', 18 | }; 19 | 20 | // Initialize Firebase 21 | const app = initializeApp(firebaseConfig); 22 | 23 | // Initialize Realtime Database and Auth 24 | const database = getFirestore(app); 25 | const auth = getAuth(app); 26 | 27 | // Sign in anonymously 28 | signInAnonymously(auth) 29 | .then(() => { 30 | console.log('Signed in anonymously to Firebase Auth'); 31 | }) 32 | .catch((error) => { 33 | console.error('Firebase Auth Error:', error); 34 | }); 35 | 36 | export { database }; 37 | -------------------------------------------------------------------------------- /src/main/association.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { app } from 'electron'; 3 | import { Registry } from 'rage-edit'; 4 | 5 | export const makeAssociation = () => { 6 | if (!app.isPackaged) return; 7 | app.setAsDefaultProtocolClient('http'); 8 | app.setAsDefaultProtocolClient('https'); 9 | app.setAsDefaultProtocolClient('bonbon'); 10 | 11 | if (process.platform === 'win32') { 12 | (async () => { 13 | await Registry.set( 14 | 'HKCU\\Software\\BonBon\\Capabilities', 15 | 'ApplicationName', 16 | 'BonBon' 17 | ); 18 | await Registry.set( 19 | 'HKCU\\Software\\BonBon\\Capabilities', 20 | 'ApplicationDescription', 21 | 'BonBon' 22 | ); 23 | 24 | await Registry.set( 25 | 'HKCU\\Software\\BonBon\\Capabilities\\URLAssociations', 26 | 'https', 27 | 'BonBon.https' 28 | ); 29 | 30 | await Registry.set( 31 | 'HKCU\\Software\\BonBon\\Capabilities\\URLAssociations', 32 | 'https', 33 | 'BonBon.http' 34 | ); 35 | 36 | await Registry.set( 37 | 'HKCU\\Software\\Classes\\BonBon.https\\DefaultIcon', 38 | '', 39 | process.execPath 40 | ); 41 | 42 | await Registry.set( 43 | 'HKCU\\Software\\Classes\\BonBon.http\\DefaultIcon', 44 | '', 45 | process.execPath 46 | ); 47 | 48 | await Registry.set( 49 | 'HKCU\\Software\\Classes\\BonBon.https\\shell\\open\\command', 50 | '', 51 | `"${process.execPath}" "%1"` 52 | ); 53 | 54 | await Registry.set( 55 | 'HKCU\\Software\\Classes\\BonBon.http\\shell\\open\\command', 56 | '', 57 | `"${process.execPath}" "%1"` 58 | ); 59 | 60 | await Registry.set( 61 | 'HKCU\\Software\\RegisteredApplications', 62 | 'BonBon', 63 | 'Software\\BonBon\\Capabilities' 64 | ); 65 | })(); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /src/main/constants.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export const DARWIN = process.platform === 'darwin'; 3 | -------------------------------------------------------------------------------- /src/main/downloads.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { Download } from 'types/downloads'; 3 | import { IpcAddDownload } from 'types/ipc'; 4 | 5 | import db from './db'; 6 | 7 | export const addDownload = (args: IpcAddDownload): Promise => { 8 | return new Promise((resolve, reject) => { 9 | db.get( 10 | 'SELECT * FROM downloads WHERE savePath = ? AND startTime = ?', 11 | args.savePath, 12 | args.startTime, 13 | (err: Error | null, row: Download[]) => { 14 | if (err) reject(new Error(`Couldn't add download: ${err.message}`)); 15 | else if (!row) { 16 | db.run( 17 | 'INSERT INTO downloads (savePath, filename, date, startTime) VALUES (?, ?, datetime("now", "localtime"), ?)', 18 | args.savePath, 19 | args.filename, 20 | args.startTime, 21 | (err2?: Error) => { 22 | if (err2) 23 | reject(new Error(`Couldn't add download: ${err2.message}`)); 24 | else resolve(); 25 | } 26 | ); 27 | } else { 28 | reject(new Error(`Couldn't add download: download already exists.`)); 29 | } 30 | } 31 | ); 32 | }); 33 | }; 34 | 35 | export const getAllDownloads = (): Promise => { 36 | return new Promise((resolve, reject) => { 37 | db.all( 38 | 'SELECT * FROM downloads ORDER BY date DESC', 39 | (err, rows: Download[]) => { 40 | if (err) reject(new Error(`Couldn't get downloads: ${err.message}`)); 41 | else resolve(rows); 42 | } 43 | ); 44 | }); 45 | }; 46 | 47 | export const clearDownloads = (): Promise => { 48 | return new Promise((resolve, reject) => { 49 | db.run('DELETE FROM downloads', (err?) => { 50 | if (err) reject(new Error(`Couldn't clear downloads: ${err.message}`)); 51 | else resolve(); 52 | }); 53 | }); 54 | }; 55 | 56 | export const removeDownload = (id: number): Promise => { 57 | return new Promise((resolve, reject) => { 58 | db.run('DELETE FROM downloads WHERE id = ?', id, (err?) => { 59 | if (err) reject(new Error(`Couldn't delete download: ${err.message}`)); 60 | else resolve(); 61 | }); 62 | }); 63 | }; 64 | -------------------------------------------------------------------------------- /src/main/i18n.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | 3 | import en from './locales/en.json'; 4 | import es from './locales/es.json'; 5 | import fr from './locales/fr.json'; 6 | import nl from './locales/nl.json'; 7 | import pl from './locales/pl.json'; 8 | import ru from './locales/ru.json'; 9 | import ar from './locales/ar.json'; 10 | import cn from './locales/cn.json'; 11 | import pt from './locales/pt.json'; 12 | import tr from './locales/tr.json'; 13 | import de from './locales/de.json'; 14 | import fa from './locales/fa.json'; 15 | import ja from './locales/ja.json'; 16 | 17 | const resources = { 18 | en: { 19 | translation: en, 20 | }, 21 | es: { 22 | translation: es, 23 | }, 24 | fr: { 25 | translation: fr, 26 | }, 27 | nl: { 28 | translation: nl, 29 | }, 30 | pl: { 31 | translation: pl, 32 | }, 33 | ru: { 34 | translation: ru, 35 | }, 36 | ar: { 37 | translation: ar, 38 | }, 39 | cn: { 40 | translation: cn, 41 | }, 42 | pt: { 43 | translation: pt, 44 | }, 45 | tr: { 46 | translation: tr, 47 | }, 48 | de: { 49 | translation: de, 50 | }, 51 | fa: { 52 | translation: fa, 53 | }, 54 | ja: { 55 | translation: ja, 56 | }, 57 | }; 58 | 59 | i18n.init({ 60 | resources, 61 | interpolation: { 62 | escapeValue: false, 63 | }, 64 | }); 65 | 66 | export default i18n; 67 | -------------------------------------------------------------------------------- /src/main/locales/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "إغلاق جميع علامات التبويب", 3 | "Close all windows": "أغلق جميع النوافذ", 4 | "Close others tabs": "أغلق علامات تبويب الآخرين", 5 | "Close others windows": "أغلق نوافذ أخرى", 6 | "Close tab": "إغلاق علامة التبويب", 7 | "Close window": "أغلق النافذة", 8 | "Distribute windows evenly": "توزيع النوافذ بالتساوي", 9 | "Inspect element": "فحص العنصر", 10 | "Rename tab": "إعادة تسمية علامة", 11 | "Toggle pin window": "تبديل تثبيت النافذة", 12 | "Close board": "إغلاق اللوحة", 13 | "Close all boards": "إغلاق جميع اللوحات", 14 | "Close others boards": "إغلاق اللوحات الأخرى", 15 | "Rename board": "إعادة تسمية اللوحة", 16 | "Save board": "حفظ اللوحة", 17 | "Set as default window size": "تعيين كحجم النافذة الافتراضي", 18 | "Reset settings": "إعادة تعيين الإعدادات" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/bn.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "সব ট্যাব বন্ধ করুন", 3 | "Close all windows": "সব উইন্ডো বন্ধ করুন", 4 | "Close others tabs": "অন্য ট্যাব বন্ধ করুন", 5 | "Close others windows": "অন্য উইন্ডো বন্ধ করুন", 6 | "Close tab": "ট্যাব বন্ধ করুন", 7 | "Close window": "উইন্ডো বন্ধ করুন", 8 | "Distribute windows evenly": "উইন্ডোগুলি সমানভাবে বিতরণ করুন", 9 | "Inspect element": "এলিমেন্ট পরীক্ষা করুন", 10 | "Rename tab": "ট্যাবের নাম পরিবর্তন করুন", 11 | "Toggle pin window": "পিন উইন্ডো টগল করুন", 12 | "Close board": "বোর্ড বন্ধ করুন", 13 | "Close all boards": "সব বোর্ড বন্ধ করুন", 14 | "Close others boards": "অন্য বোর্ড বন্ধ করুন", 15 | "Rename board": "বোর্ডের নাম পরিবর্তন করুন", 16 | "Save board": "বোর্ড সংরক্ষণ করুন", 17 | "Set as default window size": "ডিফল্ট উইন্ডো আকার হিসাবে সেট করুন", 18 | "Reset settings": "সেটিংস রিসেট করুন" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "关闭所有标签页", 3 | "Close all windows": "关闭所有窗口", 4 | "Close others tabs": "关闭其他标签页", 5 | "Close others windows": "关闭其他窗口", 6 | "Close tab": "关闭标签页", 7 | "Close window": "关闭窗口", 8 | "Distribute windows evenly": "均匀分布窗口", 9 | "Inspect element": "检查元素", 10 | "Rename tab": "重命名选项卡", 11 | "Toggle pin window": "切换固定窗口", 12 | "Close board": "关闭面板", 13 | "Close all boards": "关闭所有面板", 14 | "Close others boards": "关闭其他面板", 15 | "Rename board": "重命名面板", 16 | "Save board": "保存面板", 17 | "Set as default window size": "设为默认窗口大小", 18 | "Reset settings": "重置设置" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Alle Tabs schließen", 3 | "Close all windows": "Alle Fenster schließen", 4 | "Close others tabs": "Andere Tabs schließen", 5 | "Close others windows": "Andere Fenster schließen", 6 | "Close tab": "Tab schließen", 7 | "Close window": "Fenster schließen", 8 | "Distribute windows evenly": "Fenster gleichmäßig verteilen", 9 | "Inspect element": "Element prüfen", 10 | "Rename tab": "Registerkarte umbenennen", 11 | "Toggle pin window": "Fenster anheften umschalten", 12 | "Close board": "Tafel schließen", 13 | "Close all boards": "Alle Tafeln schließen", 14 | "Close others boards": "Andere Tafeln schließen", 15 | "Rename board": "Tafel umbenennen", 16 | "Save board": "Tafel speichern", 17 | "Set as default window size": "Als Standardfenstergröße festlegen", 18 | "Reset settings": "Einstellungen zurücksetzen" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close window": "Close window", 3 | "Toggle pin window": "Toggle pin window", 4 | "Close all windows": "Close all windows", 5 | "Close others windows": "Close others windows", 6 | "Close board": "Close board", 7 | "Close all boards": "Close all boards", 8 | "Close others bords": "Close others boards", 9 | "Rename board": "Rename board", 10 | "Save board": "Save board", 11 | "Distribute windows evenly": "Distribute windows evenly", 12 | "Inspect element": "Inspect element", 13 | "Set as default window size": "Set as default window size", 14 | "Reset settings": "Reset settings" 15 | } 16 | -------------------------------------------------------------------------------- /src/main/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Cerrar todas las pestañas", 3 | "Close all windows": "Cerrar todas las ventanas", 4 | "Close others tabs": "Cerrar otras pestañas", 5 | "Close others windows": "Cerrar otras ventanas", 6 | "Close tab": "Cerrar pestaña", 7 | "Close window": "Cerrar ventana", 8 | "Distribute windows evenly": "Distribuir las ventanas uniformemente", 9 | "Inspect element": "Inspeccionar elemento", 10 | "Rename tab": "Cambiar nombre de la pestaña", 11 | "Toggle pin window": "Alternar fijar ventana", 12 | "Close board": "Cerrar tablero", 13 | "Close all boards": "Cerrar todos los tableros", 14 | "Close others boards": "Cerrar otros tableros", 15 | "Rename board": "Renombrar tablero", 16 | "Save board": "Guardar tablero", 17 | "Set as default window size": "Establecer como tamaño de ventana predeterminado", 18 | "Reset settings": "Restablecer configuración" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/fa.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "بستن همه برگهها", 3 | "Close all windows": "تمام پنجره ها را ببندید", 4 | "Close others tabs": "بستن زبانه های دیگران", 5 | "Close others windows": "پنجره های دیگر را ببندید", 6 | "Close tab": "بستن برگه", 7 | "Close window": "پنجره بسته", 8 | "Distribute windows evenly": "توزیع ویندوز به طور مساوی", 9 | "Inspect element": "عنصر را بازرسی کنید", 10 | "Rename tab": "تغییر نام برگه", 11 | "Toggle pin window": "پنجره پین را تغییر دهید", 12 | "Close board": "تخته را ببندید", 13 | "Close all boards": "بستن تمام تخته ها", 14 | "Close others boards": "بستن تخته های دیگر", 15 | "Rename board": "نام تخته را تغییر دهید", 16 | "Save board": "ذخیره تخته", 17 | "Set as default window size": "تنظیم به عنوان اندازه پیش فرض پنجره", 18 | "Reset settings": "تنظیمات را بازنشانی کنید" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Fermer tous les onglets", 3 | "Close all windows": "Fermez toutes les fenêtres", 4 | "Close others tabs": "Fermer les autres onglets", 5 | "Close others windows": "Fermer les autres fenêtres", 6 | "Close tab": "Fermer l'onglet", 7 | "Close window": "Fermer la fenêtre", 8 | "Distribute windows evenly": "Répartir uniformément les fenêtres", 9 | "Inspect element": "Inspecter l'élément", 10 | "Rename tab": "Renommer l'onglet", 11 | "Toggle pin window": "Basculer la fenêtre d'ancrage", 12 | "Close board": "Fermer le board", 13 | "Close all boards": "Fermer tous les boards", 14 | "Close others boards": "Fermer les autres boards", 15 | "Rename board": "Renommer le board", 16 | "Save board": "Sauvegarder le board", 17 | "Set as default window size": "Définir comme taille de fenêtre par défaut", 18 | "Reset settings": "Réinitialiser les paramètres" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/hi.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "सभी टैब बंद करें", 3 | "Close all windows": "सभी विंडो बंद करें", 4 | "Close others tabs": "अन्य टैब बंद करें", 5 | "Close others windows": "अन्य विंडो बंद करें", 6 | "Close tab": "टैब बंद करें", 7 | "Close window": "विंडो बंद करें", 8 | "Distribute windows evenly": "विंडोज को समान रूप से वितरित करें", 9 | "Inspect element": "तत्व की जाँच करें", 10 | "Rename tab": "टैब का नाम बदलें", 11 | "Toggle pin window": "पिन विंडो टॉगल करें", 12 | "Close board": "बोर्ड बंद करें", 13 | "Close all boards": "सभी बोर्ड बंद करें", 14 | "Close others boards": "अन्य बोर्ड बंद करें", 15 | "Rename board": "बोर्ड का नाम बदलें", 16 | "Save board": "बोर्ड सहेजें", 17 | "Set as default window size": "डिफ़ॉल्ट विंडो आकार के रूप में सेट करें", 18 | "Reset settings": "सेटिंग रीसेट करें" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "すべてのタブを閉じる", 3 | "Close all windows": "すべてのウィンドウを閉じる", 4 | "Close others tabs": "他のタブを閉じる", 5 | "Close others windows": "他のウィンドウを閉じる", 6 | "Close tab": "タブを閉じる", 7 | "Close window": "ウィンドウを閉じる", 8 | "Distribute windows evenly": "ウィンドウを均等に分散", 9 | "Inspect element": "要素を検査する", 10 | "Rename tab": "タブの名前を変更", 11 | "Toggle pin window": "ピンウィンドウの切り替え", 12 | "Close board": "ボードを閉じる", 13 | "Close all boards": "すべてのボードを閉じる", 14 | "Close others boards": "他のボードを閉じる", 15 | "Rename board": "ボードの名前を変更", 16 | "Save board": "ボードを保存", 17 | "Set as default window size": "デフォルトのウィンドウサイズとして設定", 18 | "Reset settings": "設定をリセット" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Alle tabbladen sluiten", 3 | "Close all windows": "Alle vensters sluiten", 4 | "Close others tabs": "Andere tabbladen sluiten", 5 | "Close others windows": "Andere vensters sluiten", 6 | "Close tab": "Tabblad sluiten", 7 | "Close window": "Venster sluiten", 8 | "Distribute windows evenly": "Vensters gelijkmatig verdelen", 9 | "Inspect element": "Inspecteer het element", 10 | "Rename tab": "Tabblad hernoemen", 11 | "Toggle pin window": "Venster vastzetten in-/uitschakelen", 12 | "Close board": "Sluit bord", 13 | "Close all boards": "Alle borden sluiten", 14 | "Close others boards": "Sluit andere borden", 15 | "Rename board": "Bord hernoemen", 16 | "Save board": "Bord opslaan", 17 | "Set as default window size": "Instellen als standaard venstergrootte", 18 | "Reset settings": "Instellingen resetten" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Zamknij wszystkie karty", 3 | "Close all windows": "Zamknij wszystkie okna", 4 | "Close others tabs": "Zamknij inne karty", 5 | "Close others windows": "Zamknij inne okna", 6 | "Close tab": "Zamknij kartę", 7 | "Close window": "Zamknij okno", 8 | "Distribute windows evenly": "Rozłóż okna równomiernie", 9 | "Inspect element": "Sprawdź element", 10 | "Rename tab": "Zmień nazwę karty", 11 | "Toggle pin window": "Przełącz przypinanie okna", 12 | "Close board": "Zamknij tablicę", 13 | "Close all boards": "Zamknij wszystkie tablice", 14 | "Close others boards": "Zamknij inne tablice", 15 | "Rename board": "Zmień nazwę tablicy", 16 | "Save board": "Zapisz tablicę", 17 | "Set as default window size": "Ustaw jako domyślny rozmiar okna", 18 | "Reset settings": "Zresetuj ustawienia" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Fechar todos os separadores", 3 | "Close all windows": "Feche todas as janelas", 4 | "Close others tabs": "Fechar outras guias", 5 | "Close others windows": "Fechar outras janelas", 6 | "Close tab": "Fechar separador", 7 | "Close window": "Fechar janela", 8 | "Distribute windows evenly": "Distribua as janelas uniformemente", 9 | "Inspect element": "Inspecionar elemento", 10 | "Rename tab": "Guia Renomear", 11 | "Toggle pin window": "Alternar fixação de janela", 12 | "Close board": "Fechar quadro", 13 | "Close all boards": "Fechar todos os quadros", 14 | "Close others boards": "Fechar outros quadros", 15 | "Rename board": "Renomear quadro", 16 | "Save board": "Salvar quadro", 17 | "Set as default window size": "Definir como tamanho padrão da janela", 18 | "Reset settings": "Redefinir configurações" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Закрыть все вкладки", 3 | "Close all windows": "Закройте все окна", 4 | "Close others tabs": "Закрыть другие вкладки", 5 | "Close others windows": "Закрыть другие окна", 6 | "Close tab": "Закрыть вкладку", 7 | "Close window": "Закрыть окно", 8 | "Distribute windows evenly": "Равномерное распределение окон", 9 | "Inspect element": "Осмотреть элемент", 10 | "Rename tab": "Вкладка Переименовать", 11 | "Toggle pin window": "Переключить закрепление окна", 12 | "Close board": "Закрыть доску", 13 | "Close all boards": "Закрыть все доски", 14 | "Close others boards": "Закрыть другие доски", 15 | "Rename board": "Переименовать доску", 16 | "Save board": "Сохранить доску", 17 | "Set as default window size": "Установить как размер окна по умолчанию", 18 | "Reset settings": "Сбросить настройки" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/locales/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Close all tabs": "Tüm sekmeleri kapat", 3 | "Close all windows": "Tüm pencereleri kapat", 4 | "Close others tabs": "Diğerleri sekmelerini kapatma", 5 | "Close others windows": "Diğerlerinin pencerelerini kapatın", 6 | "Close tab": "Sekmeyi kapat", 7 | "Close window": "Pencereyi kapat", 8 | "Distribute windows evenly": "Pencereleri eşit olarak dağıtın", 9 | "Inspect element": "Elemanı inceleyin", 10 | "Rename tab": "Sekmeyi yeniden adlandır", 11 | "Toggle pin window": "Pencere sabitlemesini aç/kapat", 12 | "Close board": "Panoyu kapat", 13 | "Close all boards": "Tüm panoları kapat", 14 | "Close others boards": "Diğer panoları kapat", 15 | "Rename board": "Panoyu yeniden adlandır", 16 | "Save board": "Panoyu kaydet", 17 | "Set as default window size": "Varsayılan pencere boyutu olarak ayarla", 18 | "Reset settings": "Ayarları sıfırla" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import log from 'electron-log'; 2 | 3 | console.log = log.log; 4 | -------------------------------------------------------------------------------- /src/main/main.js: -------------------------------------------------------------------------------- 1 | require('ts-node').register({ 2 | transpileOnly: true, 3 | compilerOptions: { 4 | module: 'commonjs', 5 | }, 6 | }); 7 | 8 | require('./main.ts'); 9 | -------------------------------------------------------------------------------- /src/main/main.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/first */ 2 | /* eslint-disable global-require */ 3 | /** 4 | * This module executes inside of electron's main process. You can start 5 | * electron renderer process from here and communicate with the other processes 6 | * through IPC. 7 | * 8 | * When running `npm run build` or `npm run build:main`, this file is compiled to 9 | * `./src/main.js` using webpack. This gives us some performance wins. 10 | */ 11 | import { app } from 'electron'; 12 | // import { autoUpdater } from 'electron-updater'; 13 | 14 | import './appEvents'; 15 | import './logger'; 16 | 17 | if (app.isPackaged) { 18 | const sourceMapSupport = require('source-map-support'); 19 | sourceMapSupport.install(); 20 | } 21 | 22 | // app.on('ready', () => { 23 | // // Check for updates and notify the user 24 | // autoUpdater.checkForUpdatesAndNotify(); 25 | // }); 26 | 27 | // autoUpdater.on('update-available', () => { 28 | // dialog.showMessageBox({ 29 | // type: 'info', 30 | // title: 'Update Available', 31 | // message: 'A new version is available. Downloading now...', 32 | // }); 33 | // }); 34 | 35 | // autoUpdater.on('update-downloaded', () => { 36 | // const options = { 37 | // type: 'info' as const, 38 | // buttons: ['Restart', 'Later'], 39 | // title: 'Update Available', 40 | // message: 'A new update is available. Restart to apply the update?', 41 | // }; 42 | // dialog 43 | // .showMessageBox(options) 44 | // .then((result) => { 45 | // if (result.response === 0) { 46 | // autoUpdater.quitAndInstall(); 47 | // } 48 | // }) 49 | // .catch(console.log); 50 | // }); 51 | -------------------------------------------------------------------------------- /src/main/modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module "rage-edit"; 2 | -------------------------------------------------------------------------------- /src/main/store.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import Store from 'electron-store'; 3 | import { v4 as uuidv4 } from 'uuid'; 4 | import { ConfigKeys } from 'types/configKeys'; // Adjust the import path as necessary 5 | 6 | // Instantiate the store with the ConfigKeys interface for type safety 7 | const store = new Store(); 8 | 9 | // Function to retrieve the store instance 10 | export const getStore = () => store; 11 | 12 | // Initialize default configuration values if they are undefined 13 | if (store.get('browsing.defaultWebpage') === undefined) { 14 | store.set('browsing.defaultWebpage', 'https://www.google.com'); 15 | } 16 | 17 | if (store.get('browsing.searchEngine') === undefined) { 18 | store.set('browsing.searchEngine', 'google'); 19 | } 20 | 21 | if (store.get('browsing.width') === undefined) { 22 | store.set('browsing.width', 600); 23 | } 24 | 25 | if (store.get('browsing.height') === undefined) { 26 | store.set('browsing.height', 800); 27 | } 28 | 29 | if ( 30 | store.get('browsing.size') === undefined || 31 | store.get('browsing.size') === 'fit' // Fix related to issue #341 32 | ) { 33 | store.set('browsing.size', 'lastResized'); 34 | } 35 | 36 | if (store.get('browsing.topEdge') === undefined) { 37 | store.set('browsing.topEdge', 'fit'); 38 | } 39 | 40 | if (store.get('application.backgroundGradientColors') === undefined) { 41 | store.set('application.backgroundGradientColors', [ 42 | '#fedc2a', 43 | '#dd5789', 44 | '#7a2c9e', 45 | ]); 46 | } 47 | 48 | if (store.get('application.minimapTimeout') === undefined) { 49 | store.set('application.minimapTimeout', 600); 50 | } 51 | 52 | if (store.get('application.minimapOn') === undefined) { 53 | store.set('application.minimapOn', true); 54 | } 55 | 56 | if (store.get('application.forceMacosStyle') === undefined) { 57 | store.set('application.forceMacosStyle', false); 58 | } 59 | 60 | if (store.get('chat.userId') === undefined) { 61 | const randomUserId = uuidv4(); 62 | store.set('chat.userId', randomUserId); 63 | store.set('chat.username', `User_${randomUserId.slice(0, 5)}`); 64 | } 65 | -------------------------------------------------------------------------------- /src/main/util.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: off, import/no-mutable-exports: off */ 2 | import { URL } from 'url'; 3 | import path from 'path'; 4 | import fs from 'fs'; 5 | import https from 'https'; 6 | import { app } from 'electron'; 7 | 8 | export let resolveHtmlPath: (htmlFileName: string) => string; 9 | 10 | if (process.env.NODE_ENV === 'development') { 11 | const port = process.env.PORT || 1212; 12 | resolveHtmlPath = (htmlFileName: string) => { 13 | const url = new URL(`http://localhost:${port}`); 14 | url.pathname = htmlFileName; 15 | return url.href; 16 | }; 17 | } else { 18 | resolveHtmlPath = (htmlFileName: string) => { 19 | return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`; 20 | }; 21 | } 22 | 23 | export const downloadFile = (from: string, to: string): Promise => { 24 | return new Promise((resolve, reject) => { 25 | const req = https.get(from); 26 | // eslint-disable-next-line consistent-return 27 | req.on('response', (res) => { 28 | if ( 29 | res.statusCode && 30 | res.statusCode >= 300 && 31 | res.statusCode < 400 && 32 | res.headers.location 33 | ) { 34 | return downloadFile(res.headers.location, to) 35 | .then(resolve) 36 | .catch(reject); 37 | } 38 | res.pipe(fs.createWriteStream(to)).on('close', resolve); 39 | res.on('error', reject); 40 | return true; 41 | }); 42 | req.on('error', reject); 43 | req.end(); 44 | }); 45 | }; 46 | 47 | export const changePermissions = (dir: string, mode: string | number) => { 48 | const files = fs.readdirSync(dir); 49 | files.forEach((file) => { 50 | const filePath = path.join(dir, file); 51 | fs.chmodSync(filePath, parseInt(`${mode}`, 8)); 52 | if (fs.statSync(filePath).isDirectory()) { 53 | changePermissions(filePath, mode); 54 | } 55 | }); 56 | }; 57 | 58 | export const getPath = (): string => { 59 | const savePath = app.getPath('userData'); 60 | return path.resolve(`${savePath}/extensions`); 61 | }; 62 | 63 | export const isValidHttpUrl = (s: string): boolean => { 64 | let url; 65 | 66 | try { 67 | url = new URL(s); 68 | } catch (_) { 69 | return false; 70 | } 71 | 72 | return url.protocol === 'http:' || url.protocol === 'https:'; 73 | }; 74 | 75 | export const isValidUrl = (s: string): boolean => { 76 | return s.toLowerCase().indexOf('bonbon://') === 0 || isValidHttpUrl(s); 77 | }; 78 | -------------------------------------------------------------------------------- /src/namespaces/_electronist.ts: -------------------------------------------------------------------------------- 1 | import { 2 | app, 3 | BrowserWindow as browserWindow, 4 | ipcMain, 5 | ipcRenderer, 6 | shell, 7 | WebContents as webContents, 8 | Menu as menu, 9 | MenuItem as menuItem, 10 | Tray as tray, 11 | dialog, 12 | clipboard, 13 | nativeImage, 14 | screen, 15 | session, 16 | powerMonitor, 17 | powerSaveBlocker, 18 | autoUpdater, 19 | systemPreferences, 20 | nativeTheme, 21 | crashReporter, 22 | webFrame, 23 | } from 'electron'; 24 | 25 | export namespace Electron { 26 | export const App = app; 27 | export const BrowserWindow = browserWindow; 28 | export const IpcMain = ipcMain; 29 | export const IpcRenderer = ipcRenderer; 30 | export const Shell = shell; 31 | export type WebContents = webContents; 32 | export const Menu = menu; 33 | export const MenuItem = menuItem; 34 | export const Tray = tray; 35 | export const Dialog = dialog; 36 | export const Clipboard = clipboard; 37 | export const NativeImage = nativeImage; 38 | export const Screen = screen; 39 | export const Session = session; 40 | export const PowerMonitor = powerMonitor; 41 | export const PowerSaveBlocker = powerSaveBlocker; 42 | export const AutoUpdater = autoUpdater; 43 | export const SystemPreferences = systemPreferences; 44 | export const NativeTheme = nativeTheme; 45 | export const CrashReporter = crashReporter; 46 | export const WebFrame = webFrame; 47 | 48 | // by danny bengal 49 | export type WebviewTag = any 50 | export type Extension = any 51 | export type DownloadItem = any 52 | } 53 | -------------------------------------------------------------------------------- /src/renderer/App/App.css: -------------------------------------------------------------------------------- 1 | /* 2 | * @NOTE: Prepend a `~` to css file paths that are in your node_modules 3 | * See https://github.com/webpack-contrib/sass-loader#imports 4 | */ 5 | body { 6 | font-family: sans-serif; 7 | margin: 0; 8 | } 9 | 10 | .bold { 11 | font-weight: bold; 12 | } 13 | 14 | .MuiButton-contained { 15 | background-color: var(--color-icon-primary) !important; 16 | } 17 | 18 | .justify-content-right { 19 | div, ul, li, input { 20 | justify-content: right; 21 | text-align: right; 22 | } 23 | } 24 | 25 | .MuiInputBase-root { 26 | div, svg { 27 | color: var(--color-text-primary) !important; 28 | } 29 | 30 | fieldset { 31 | border-color: var(--color-border); 32 | } 33 | } 34 | 35 | .MuiTextField-root { 36 | input { 37 | background-color: var(--color-icon-secondary); 38 | } 39 | } 40 | 41 | .MuiChip-filled { 42 | background-color: var(--color-background-tertiary) !important; 43 | color: var(--color-text-primary) !important; 44 | } 45 | -------------------------------------------------------------------------------- /src/renderer/App/components/About/About.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { useTranslation } from 'react-i18next'; 3 | 4 | import packagejson from '../../../../../package.json'; 5 | 6 | import './style.scss'; 7 | 8 | export const About = () => { 9 | const { t } = useTranslation(); 10 | 11 | return ( 12 | <> 13 |
14 | {t('App version')}:{' '} 15 | {packagejson.version} 16 |
17 |
18 | {t('Author')}: Daniel Febrero 19 |
20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/renderer/App/components/About/index.tsx: -------------------------------------------------------------------------------- 1 | import { About } from './About'; 2 | 3 | export * from './About'; 4 | export default About; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/About/style.scss: -------------------------------------------------------------------------------- 1 | .About__property-line { 2 | margin-bottom: 5px;; 3 | 4 | .About__property { 5 | font-weight: bold; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/App/components/Addaps/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface AddapsProps { 2 | boardId?: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Addaps/index.tsx: -------------------------------------------------------------------------------- 1 | import { Addaps } from './Addaps'; 2 | 3 | export * from './Addaps'; 4 | export default Addaps; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Addaps/style.css: -------------------------------------------------------------------------------- 1 | #Addaps__background { 2 | background: linear-gradient( 3 | 200.96deg, 4 | #fedc2a -29.09%, 5 | #dd5789 51.77%, 6 | #7a2c9e 129.35% 7 | ); 8 | height: 100%; 9 | width: 100%; 10 | position: fixed; 11 | } 12 | 13 | #Minimap__detection-zone { 14 | height: 100%; 15 | width: 20px; 16 | background: transparent; 17 | position: fixed; 18 | right: 0; 19 | } 20 | -------------------------------------------------------------------------------- /src/renderer/App/components/AppMenu/AppMenu.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | /* eslint-disable import/prefer-default-export */ 4 | import { useTranslation } from 'react-i18next'; 5 | 6 | import { AppMenuProps } from './Types'; 7 | 8 | import './style.scss'; 9 | 10 | export const AppMenu = ({ 11 | showAbout, 12 | showSettings, 13 | showBookmarks, 14 | showHistory, 15 | showDownloads, 16 | showDocumentation, 17 | showExtensions, 18 | showBoards, 19 | }: AppMenuProps) => { 20 | const { t } = useTranslation(); 21 | 22 | return ( 23 |
28 |
    29 |
  • window.app.tools.toggleDarkMode()}> 30 | {t('Toggle dark mode')} 31 |
  • 32 |
  • showBoards()}>{t('Boards')}
  • 33 |
  • showExtensions()}>{t('Extensions')}
  • 34 |
  • showDownloads()}>{t('Downloads')}
  • 35 |
  • showBookmarks()}>{t('Bookmarks')}
  • 36 |
  • showHistory()}>{t('History')}
  • 37 |
  • showSettings()}>{t('Settings')}
  • 38 |
  • showDocumentation()}>{t('Documentation')}
  • 39 |
  • showAbout()}>{t('About')}
  • 40 |
41 |
42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/renderer/App/components/AppMenu/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface AppMenuProps { 2 | showBookmarks: () => void; 3 | showSettings: () => void; 4 | showAbout: () => void; 5 | showHistory: () => void; 6 | showDownloads: () => void; 7 | showDocumentation: () => void; 8 | showExtensions: () => void; 9 | showBoards: () => void; 10 | } 11 | -------------------------------------------------------------------------------- /src/renderer/App/components/AppMenu/index.tsx: -------------------------------------------------------------------------------- 1 | import { AppMenu } from './AppMenu'; 2 | 3 | export * from './AppMenu'; 4 | export default AppMenu; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/AppMenu/style.scss: -------------------------------------------------------------------------------- 1 | #AppMenu__container { 2 | position: fixed; 3 | right: 0; 4 | top: 0; 5 | background-color: var(--color-background-tertiary); 6 | width: 200px; 7 | color: var(--color-text-primary); 8 | z-index: 100; 9 | 10 | ul { 11 | list-style: none; 12 | padding: 0; 13 | margin: 0; 14 | 15 | li { 16 | padding: 13px; 17 | border-top: 1px solid var(--color-text-primary); 18 | cursor: pointer; 19 | } 20 | 21 | li:hover { 22 | background-color: var(--color-background-5); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/renderer/App/components/Board/Types.d.ts: -------------------------------------------------------------------------------- 1 | import { BrowserProps } from '../Browser/Types'; 2 | 3 | export interface BoardType { 4 | id: string; 5 | label: string; 6 | browsers: BrowserProps[]; 7 | activeBrowser?: string | null; 8 | closedUrls: string[]; 9 | isFullSize: boolean; 10 | lastClosedBrowserDimensions?: [number, number]; 11 | lastResizedBrowserDimensions?: [number, number]; 12 | browsersActivity: string[]; 13 | height: number; 14 | isInAppMenu: boolean; 15 | showMagicChat: boolean; 16 | } 17 | 18 | export interface BoardProps { 19 | isFullSize?: boolean; 20 | boardId?: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/App/components/Board/index.tsx: -------------------------------------------------------------------------------- 1 | import { Board } from './Board'; 2 | 3 | export * from './Board'; 4 | export default Board; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Board/style.scss: -------------------------------------------------------------------------------- 1 | #Board__container { 2 | display: flex; 3 | width: calc(100% - 50px); 4 | top: 0px; 5 | left: 50px; 6 | position: absolute; 7 | overflow: hidden; 8 | 9 | &.Board__minimap-always-on { 10 | width: calc(100% - 250px); 11 | 12 | .Board__edge-snap-right { 13 | right: 210px; 14 | } 15 | } 16 | 17 | .Board__edge-snap-left, 18 | .Board__edge-snap-right, 19 | .Board__edge-snap-maximized { 20 | top: 10px; 21 | border: 4px solid var(--color-text-primary); 22 | position: fixed; 23 | z-index: 99; 24 | width: 25vw; 25 | height: 25vh; 26 | border-radius: 25px; 27 | background: var(--color-background-primary); 28 | transition: width 0.24s, height 0.2s, opacity 0.04s ease; 29 | opacity: 0; 30 | pointer-events: none; 31 | } 32 | 33 | .Board__edge-snap-left { 34 | left: 60px; 35 | } 36 | 37 | .Board__edge-snap-right { 38 | right: 10px; 39 | } 40 | 41 | .Board__edge-snap-maximized { 42 | left: 60px; 43 | } 44 | } 45 | 46 | .Board__is-maximized { 47 | height: 100%; 48 | max-height: 100%; 49 | } 50 | 51 | .Board__isnt-maximized { 52 | min-height: 1000vh; 53 | } 54 | 55 | .Browser__draggable-container { 56 | min-width: 300px; 57 | } -------------------------------------------------------------------------------- /src/renderer/App/components/Boards/BoardsItem/BoardsItem.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable promise/always-return */ 2 | /* eslint-disable react/jsx-props-no-spreading */ 3 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 4 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 5 | /* eslint-disable import/prefer-default-export */ 6 | import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; 7 | 8 | import { BoardsItemProps } from './Types'; 9 | 10 | export const BoardsItem = ({ 11 | board, 12 | handleDelete, 13 | handleClick, 14 | }: BoardsItemProps) => { 15 | 16 | return ( 17 |
18 |
handleClick(board.id)} 21 | > 22 |
{board.label}
23 |
24 |
25 |
handleDelete(board.id)} 28 | > 29 | 30 |
31 |
32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/renderer/App/components/Boards/BoardsItem/Types.d.ts: -------------------------------------------------------------------------------- 1 | import { Board } from "types/boards"; 2 | 3 | export interface BoardsItemProps { 4 | board: Board; 5 | handleDelete: (id: string) => void; 6 | handleClick: (url: string) => void; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/renderer/App/components/Boards/BoardsItem/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BoardsItem'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Boards/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BoardsProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Boards/index.ts: -------------------------------------------------------------------------------- 1 | import { Boards } from './Boards'; 2 | 3 | export * from './Boards'; 4 | export default Boards; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Boards/style.scss: -------------------------------------------------------------------------------- 1 | #Boards__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | overflow-y: auto; 12 | 13 | #Boards__centered-container { 14 | display: flex; 15 | width: 800px; 16 | flex-direction: column; 17 | flex-grow: 1; 18 | 19 | .Boards__search { 20 | margin-bottom: 30px; 21 | border-radius: 3px; 22 | height: 30px; 23 | font-size: 15px; 24 | padding-left: 20px; 25 | padding-right: 20px; 26 | } 27 | 28 | .Boards_import-button { 29 | width: fit-content; 30 | align-self: flex-end; 31 | margin-bottom: 30px; 32 | } 33 | } 34 | } 35 | 36 | .Boards__item { 37 | background-color: var(--color-background-primary); 38 | padding: 20px; 39 | border-radius: 3px; 40 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 41 | margin-bottom: 30px; 42 | cursor: pointer; 43 | display: flex; 44 | flex-direction: row; 45 | overflow: hidden; 46 | 47 | .Boards__item-text { 48 | flex-grow: 1; 49 | overflow: hidden; 50 | 51 | .Boards__item-tags { 52 | display: flex; 53 | flex-direction: row; 54 | margin-top: 10px; 55 | 56 | .MuiChip-root { 57 | margin-right: 10px; 58 | } 59 | } 60 | 61 | .Boards__item-name { 62 | font-weight: bold; 63 | } 64 | 65 | .Boards__item-url { 66 | overflow: hidden; 67 | overflow-wrap: break-word; 68 | height: 18px; 69 | } 70 | } 71 | 72 | .Boards__item-controls { 73 | display: flex; 74 | flex-direction: row; 75 | 76 | .Boards__item-control { 77 | padding: 10px; 78 | } 79 | 80 | .Boards__item-control:hover { 81 | color: var(--color-icon-primary); 82 | } 83 | } 84 | 85 | } 86 | 87 | .Boards__item:hover{ 88 | border: 1px solid var(--color-border); 89 | } 90 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/BookmarksItem/Types.d.ts: -------------------------------------------------------------------------------- 1 | import { Bookmark } from "types/bookmarks"; 2 | 3 | export interface BookmarksItemProps { 4 | bookmark: Bookmark; 5 | handleDelete: (id: number) => void; 6 | handleClick: (url: string) => void; 7 | replaceItem: (bookmark: Bookmark) => void; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/BookmarksItem/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BookmarksItem'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/Import/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface ImportProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/Import/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Import'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/Import/style.scss: -------------------------------------------------------------------------------- 1 | #Import__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 11; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | overflow-y: auto; 12 | 13 | #Import__centered-container { 14 | display: flex; 15 | width: 800px; 16 | flex-direction: column; 17 | flex-grow: 1; 18 | 19 | .Import__search { 20 | margin-bottom: 30px; 21 | border-radius: 3px; 22 | height: 30px; 23 | font-size: 15px; 24 | padding-left: 20px; 25 | padding-right: 20px; 26 | } 27 | 28 | .Import__action-line { 29 | width: 100%; 30 | display: flex; 31 | flex-direction: row; 32 | align-items: center; 33 | } 34 | 35 | .Import_providers-list { 36 | min-width: 100px; 37 | margin-left: 15px; 38 | } 39 | 40 | .Import_import-all-button { 41 | width: fit-content; 42 | align-self: flex-end; 43 | margin-bottom: 30px; 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BookmarksProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/index.ts: -------------------------------------------------------------------------------- 1 | import { Bookmarks } from './Bookmarks'; 2 | 3 | export * from './Bookmarks'; 4 | export default Bookmarks; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Bookmarks/style.scss: -------------------------------------------------------------------------------- 1 | #Bookmarks__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | overflow-y: auto; 12 | 13 | #Bookmarks__centered-container { 14 | display: flex; 15 | width: 800px; 16 | flex-direction: column; 17 | flex-grow: 1; 18 | 19 | .Bookmarks__search { 20 | margin-bottom: 30px; 21 | border-radius: 3px; 22 | height: 30px; 23 | font-size: 15px; 24 | padding-left: 20px; 25 | padding-right: 20px; 26 | } 27 | 28 | .Bookmarks_import-button { 29 | width: fit-content; 30 | align-self: flex-end; 31 | margin-bottom: 30px; 32 | } 33 | } 34 | } 35 | 36 | .Bookmarks__item { 37 | background-color: var(--color-background-primary); 38 | padding: 20px; 39 | border-radius: 3px; 40 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 41 | margin-bottom: 30px; 42 | cursor: pointer; 43 | display: flex; 44 | flex-direction: row; 45 | overflow: hidden; 46 | 47 | .Bookmarks__item-text { 48 | flex-grow: 1; 49 | overflow: hidden; 50 | 51 | .Bookmarks__item-tags { 52 | display: flex; 53 | flex-direction: row; 54 | margin-top: 10px; 55 | 56 | .MuiChip-root { 57 | margin-right: 10px; 58 | } 59 | } 60 | 61 | .Bookmarks__item-name { 62 | font-weight: bold; 63 | } 64 | 65 | .Bookmarks__item-url { 66 | overflow: hidden; 67 | overflow-wrap: break-word; 68 | height: 18px; 69 | } 70 | } 71 | 72 | .Bookmarks__item-controls { 73 | display: flex; 74 | flex-direction: row; 75 | 76 | .Bookmarks__item-control { 77 | padding: 10px; 78 | } 79 | 80 | .Bookmarks__item-control:hover { 81 | color: var(--color-icon-primary); 82 | } 83 | } 84 | 85 | } 86 | 87 | .Bookmarks__item:hover{ 88 | border: 1px solid var(--color-border); 89 | } 90 | -------------------------------------------------------------------------------- /src/renderer/App/components/Browser/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BrowserProps { 2 | id: string; 3 | url: string; 4 | top: number; 5 | left: number; 6 | height: number; 7 | width: number; 8 | firstRendering?: boolean; 9 | favicon?: string; 10 | title?: string; 11 | webContentsId?: number; 12 | isLoading: boolean; 13 | isMinimized: boolean; 14 | certificateErrorFingerprint?: string | null; 15 | isSearching?: boolean; 16 | capture?: string; 17 | session?: string; 18 | isPinned: boolean; 19 | incognito?: boolean; 20 | } 21 | -------------------------------------------------------------------------------- /src/renderer/App/components/Browser/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Browser'; 2 | export { Browser as default } from './Browser'; 3 | -------------------------------------------------------------------------------- /src/renderer/App/components/Browser/style.scss: -------------------------------------------------------------------------------- 1 | webview { 2 | display: flex; 3 | width: 100%; 4 | background-color: white; 5 | } 6 | 7 | .Browser__container { 8 | width: auto; 9 | height: auto; 10 | display: flex; 11 | flex-direction: column; 12 | flex-grow: 1; 13 | border: 1px solid var(--color-border); 14 | } 15 | 16 | .Browser__webview-container { 17 | display: flex; 18 | flex-grow: 1; 19 | } 20 | 21 | .Browser__is-full-size { 22 | position: fixed !important; 23 | top: 0px !important; 24 | width: calc(100% - 50px) !important; 25 | height: 100% !important; 26 | transform: none !important; 27 | left: 50px !important; 28 | } 29 | 30 | .Browser__is-minimized { 31 | display: none !important; 32 | } 33 | 34 | .Browser__display-none { 35 | display: none !important; 36 | } 37 | 38 | .Browser__is-pinned { 39 | position: fixed !important; 40 | margin-left: 50px; 41 | } -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserControlBar/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BrowserControlBarProps { 2 | url: string; 3 | browserId: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserControlBar/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BrowserControlBar'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserControlBar/style.scss: -------------------------------------------------------------------------------- 1 | .BrowserControlBar__container { 2 | height: 25px; 3 | display: flex; 4 | flex-direction: row; 5 | background-color: var(--color-background-secondary); 6 | 7 | .BrowserControlBar__bookmark-control { 8 | position: absolute; 9 | right: 10px; 10 | color: var(--color-icon-primary); 11 | } 12 | } 13 | 14 | .BrowserControlBar__controls { 15 | display: flex; 16 | flex-direction: row; 17 | width: 200px; 18 | justify-content: space-around; 19 | align-items: center; 20 | color: var(--color-text-primary); 21 | 22 | .BrowserControlBar__control { 23 | padding: 0px 10px; 24 | border-radius: 3px; 25 | cursor: pointer; 26 | 27 | svg { 28 | height: 18px; 29 | } 30 | 31 | svg:hover { 32 | height: 20px; 33 | } 34 | } 35 | 36 | .BrowserControlBar__control:hover { 37 | background-color: var(--color-background-5); 38 | } 39 | } 40 | 41 | .BrowserControlBar__container .MuiInput-root { 42 | margin-top: 0 !important; 43 | } 44 | 45 | .BrowserControlBar_url-input { 46 | flex-grow: 1; 47 | user-select: none; 48 | 49 | input { 50 | font-size: 14px; 51 | background-color: var(--color-icon-secondary); 52 | color: var(--color-text-secondary); 53 | border-radius: 3px; 54 | height: 15px; 55 | padding-left: 5px; 56 | padding-right: 30px; 57 | margin-right: 5px; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserInputSuggestions/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BrowserInputSuggestionsProps { 2 | inputValue: string; 3 | handleSuggestionClick: (url: string) => void; 4 | setSelectedSuggestion: (url: string | null) => void; 5 | setDomainSuggestionResults: (items: SuggestionItem[]) => void; 6 | } 7 | 8 | export type HistoryItem = { 9 | id: number; 10 | url: string; 11 | date: string; 12 | } 13 | 14 | export type SuggestionItem = { 15 | id: string; 16 | url?: string; 17 | display?: string; 18 | type: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserInputSuggestions/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BrowserInputSuggestions'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserInputSuggestions/style.scss: -------------------------------------------------------------------------------- 1 | .BrowserInputSuggestions__container { 2 | position: absolute; 3 | top: 50px; 4 | right: 0px; 5 | width: calc(100% - 206px); 6 | margin-right: 6px; 7 | border-radius: 3px; 8 | overflow: hidden; 9 | 10 | ul { 11 | list-style: none; 12 | margin: 0; 13 | width: 100%; 14 | padding: 0; 15 | font-size: 14px; 16 | 17 | li { 18 | padding: 3px; 19 | background-color: var(--color-icon-secondary); 20 | cursor: pointer; 21 | overflow: hidden; 22 | height: 15px; 23 | } 24 | 25 | li:hover { 26 | background-color: var(--color-border); 27 | } 28 | 29 | .BrowserInputSuggestions__selected-item { 30 | background-color: var(--color-border); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserTopBar/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BrowserTopBarProps { 2 | closeBrowser: () => void; 3 | minimizeBrowser: () => void; 4 | toggleFullSizeBrowser: () => void; 5 | onClick: () => void; 6 | title?: string; 7 | favicon?: string; 8 | isLoading: boolean; 9 | isMaximized: boolean; 10 | isPinned: boolean; 11 | } 12 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserTopBar/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BrowserTopBar'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/BrowserTopBar/style.scss: -------------------------------------------------------------------------------- 1 | .BrowserTopBar__container { 2 | background-color: var(--color-background-primary); 3 | height: 25px; 4 | display: flex; 5 | flex-direction: row; 6 | padding-left: 10px; 7 | cursor: grab; 8 | 9 | &.macos { 10 | padding-left: 0px; 11 | } 12 | } 13 | 14 | .BrowserTopBar__title { 15 | display: flex; 16 | flex-grow: 1; 17 | color: var(--color-text-primary); 18 | align-items: center; 19 | text-align: left !important; 20 | justify-content: left !important; 21 | font-size: 14px; 22 | } 23 | 24 | .BrowserTopBar__controls { 25 | display: flex; 26 | flex-direction: row-reverse; 27 | align-items: center; 28 | 29 | &.macos { 30 | flex-direction: row !important; 31 | margin-right: 10px; 32 | } 33 | } 34 | 35 | 36 | .BrowserTopBar__control-button { 37 | color: var(--color-icon-primary); 38 | padding: 2px 15px; 39 | border-radius: 3px; 40 | cursor: pointer; 41 | 42 | svg { 43 | height: 18px; 44 | } 45 | 46 | svg:hover { 47 | height: 20px; 48 | } 49 | } 50 | 51 | .BrowserTopBar__control-button:hover { 52 | background-color: var(--color-background-5); 53 | } 54 | 55 | .BrowserTopBar__favicon { 56 | margin-right: 5px; 57 | margin-top: 4px; 58 | } 59 | 60 | .close-button:hover { 61 | background-color: rgba(255, 0, 0, 0.5) !important; 62 | } -------------------------------------------------------------------------------- /src/renderer/App/components/ButtonAddBrowser/ButtonAddBrowser.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/button-has-type */ 2 | /* eslint-disable import/prefer-default-export */ 3 | /* eslint-disable no-use-before-define */ 4 | import AddIcon from '@mui/icons-material/Add'; 5 | 6 | import { ButtonAddBrowserProps } from './Types'; 7 | 8 | import './style.css'; 9 | 10 | export const ButtonAddBrowser = ({ onClick }: ButtonAddBrowserProps) => { 11 | return onClick({})} />; 12 | }; 13 | -------------------------------------------------------------------------------- /src/renderer/App/components/ButtonAddBrowser/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface ButtonAddBrowserProps { 2 | onClick: ({ url }: { url?: string }) => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/ButtonAddBrowser/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './ButtonAddBrowser'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/ButtonAddBrowser/style.css: -------------------------------------------------------------------------------- 1 | #ButtonAddBrowser { 2 | height: 18px; 3 | color: var(--color-icon-primary); 4 | padding: 7px; 5 | border-radius: 3px; 6 | cursor: pointer; 7 | } 8 | 9 | #ButtonAddBrowser:hover { 10 | height: 20px; 11 | background-color: var(--color-background-5); 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/App/components/CertificateErrorPage/CertificateErrorPage.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { Button } from '@mui/material'; 3 | import { useTranslation } from 'react-i18next'; 4 | 5 | import { useAppDispatch } from 'renderer/App/store/hooks'; 6 | import { updateBrowserCertificateErrorFingerprint } from 'renderer/App/store/reducers/Board'; 7 | 8 | import './style.scss'; 9 | 10 | import { CertificateErrorPageProps } from './Types'; 11 | 12 | export const CertificateErrorPage = ({ 13 | webContentsId, 14 | browserId, 15 | fingerprint, 16 | reload, 17 | }: CertificateErrorPageProps) => { 18 | const { t } = useTranslation(); 19 | const dispatch = useAppDispatch(); 20 | 21 | const handleContinue = () => { 22 | console.log(webContentsId, fingerprint); 23 | window.app.browser.certificateErrorAnswer({ 24 | webContentsId, 25 | fingerprint, 26 | isTrusted: true, 27 | }); 28 | 29 | dispatch( 30 | updateBrowserCertificateErrorFingerprint({ 31 | browserId, 32 | certificateErrorFingerprint: null, 33 | }) 34 | ); 35 | 36 | reload(); 37 | }; 38 | 39 | return ( 40 |
41 |
42 | {t( 43 | 'The certificate for this page is incorrect. Visiting the website could be dangerous.' 44 | )} 45 |
46 |
47 | 50 |
51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/renderer/App/components/CertificateErrorPage/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface CertificateErrorPageProps { 2 | webContentsId: number; 3 | browserId: string; 4 | fingerprint: string; 5 | reload: () => void; 6 | } 7 | -------------------------------------------------------------------------------- /src/renderer/App/components/CertificateErrorPage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CertificateErrorPage'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/CertificateErrorPage/style.scss: -------------------------------------------------------------------------------- 1 | .CertificateErrorPage__container { 2 | display: flex; 3 | flex-grow: 1; 4 | position: absolute; 5 | height: 100%; 6 | width: 100%; 7 | background-color: var(--color-background-primary); 8 | color: var(--color-text-primary); 9 | align-items: center; 10 | justify-content: center; 11 | flex-direction: column; 12 | z-index: 100; 13 | 14 | .CertificateErrorPage__message { 15 | margin-bottom: 30px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/renderer/App/components/Chat/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface ChatProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Chat/index.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from './Chat'; 2 | 3 | export * from './Chat'; 4 | export default Chat; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/CloseButton/CloseButton.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 2 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 3 | /* eslint-disable import/prefer-default-export */ 4 | import CloseIcon from '@mui/icons-material/Close'; 5 | import clsx from 'clsx'; 6 | 7 | import { CloseButtonProps } from './Types'; 8 | 9 | import './style.scss'; 10 | 11 | export const CloseButton = ({ handleClose, customClass }: CloseButtonProps) => { 12 | return ( 13 |
18 | 19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/renderer/App/components/CloseButton/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface CloseButtonProps { 2 | handleClose: () => void; 3 | customClass?: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/CloseButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CloseButton'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/CloseButton/style.scss: -------------------------------------------------------------------------------- 1 | .CloseButton__container { 2 | position: absolute; 3 | padding: 10px; 4 | right: 0; 5 | top: 0; 6 | cursor: pointer; 7 | 8 | svg { 9 | height: 18px; 10 | } 11 | } 12 | 13 | .CloseButton__with-scrollbar { 14 | right: 17px !important; 15 | } 16 | 17 | .CloseButton__container:hover{ 18 | background-color: rgba(255, 0, 0, 0.5); 19 | 20 | svg { 21 | height: 20px; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/Documentation.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | /* eslint-disable import/prefer-default-export */ 4 | import { useState, ReactElement, useEffect } from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | import clsx from 'clsx'; 7 | 8 | import { CloseButton } from 'renderer/App/components/CloseButton'; 9 | import { KeyboardShortcuts } from './KeyboardShortcuts'; 10 | import { WebpagesWindows } from './WebpagesWindows'; 11 | 12 | import './style.scss'; 13 | 14 | import { DocumentationProps } from './Types'; 15 | 16 | export const Documentation = ({ handleClose }: DocumentationProps) => { 17 | const { t } = useTranslation(); 18 | const [selectedView, setSelectedView] = useState( 19 | 20 | ); 21 | const [scrollbarVisible, setScrollBarVisible] = useState( 22 | false 23 | ); 24 | 25 | const isScrollbarVisible = (element: HTMLElement | null) => { 26 | return element && element.scrollHeight > element.clientHeight; 27 | }; 28 | 29 | useEffect(() => { 30 | setScrollBarVisible( 31 | isScrollbarVisible(document.getElementById('Documentation__right-panel')) 32 | ); 33 | }, []); 34 | 35 | return ( 36 |
37 | 43 |
44 |

{t('Documentation')}

45 |
    46 |
  • setSelectedView()}> 47 | {t('Keyboard shortcuts')} 48 |
  • 49 |
  • setSelectedView()} 51 | data-testid="documentation-webpages-link" 52 | > 53 | {t('Webpages windows')} 54 |
  • 55 |
56 |
57 |
{selectedView}
58 |
59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/KeyboardShortcuts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './KeyboardShortcuts'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface DocumentationProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/WebpagesWindows/WebpagesWindows.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { useTranslation } from 'react-i18next'; 3 | 4 | export const WebpagesWindows = () => { 5 | const { t } = useTranslation(); 6 | 7 | return ( 8 | <> 9 |

{t('Webpages windows')}

10 |
11 |
12 | Distribute webpages windows evenly 13 |
14 |
15 | {t( 16 | 'Right click on the background document (not on a webpage), and select "Distribute windows evenly".' 17 | )} 18 |
19 |
20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/WebpagesWindows/index.ts: -------------------------------------------------------------------------------- 1 | export * from './WebpagesWindows'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/index.ts: -------------------------------------------------------------------------------- 1 | import { Documentation } from './Documentation'; 2 | 3 | export * from './Documentation'; 4 | export default Documentation; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Documentation/style.scss: -------------------------------------------------------------------------------- 1 | #Documentation__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: row; 10 | 11 | .Documentation__item { 12 | background-color: var(--color-background-primary); 13 | padding: 20px; 14 | border-radius: 3px; 15 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 16 | margin-bottom: 30px; 17 | 18 | .Documentation__item-description { 19 | font-style: italic; 20 | margin-top: 10px; 21 | } 22 | 23 | .Documentation__item-title { 24 | font-weight: bold; 25 | } 26 | } 27 | 28 | #Documentation__left-panel { 29 | width: 300px; 30 | height: 100%; 31 | border-right: 1px solid var(--color-border); 32 | padding: 50px; 33 | 34 | ul { 35 | list-style: none; 36 | margin: 0; 37 | padding: 0; 38 | 39 | li { 40 | cursor: pointer; 41 | padding: 5px; 42 | border-radius: 3px; 43 | } 44 | 45 | li:hover { 46 | background-color: var(--color-background-primary); 47 | } 48 | } 49 | } 50 | 51 | #Documentation__right-panel { 52 | flex-grow: 1; 53 | height: calc(100% - 100px); 54 | padding: 50px; 55 | overflow-y: scroll; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/renderer/App/components/Downloads/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface DownloadsProps { 2 | handleClose: () => void; 3 | } 4 | 5 | export type DownloadType = { 6 | id: number; 7 | savePath: string; 8 | date: string; 9 | filename: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/renderer/App/components/Downloads/index.ts: -------------------------------------------------------------------------------- 1 | import { Downloads } from './Downloads'; 2 | 3 | export * from './Downloads'; 4 | export default Downloads; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Downloads/style.scss: -------------------------------------------------------------------------------- 1 | #Downloads__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | overflow-y: auto; 12 | 13 | #Downloads__centered-container { 14 | display: flex; 15 | width: 800px; 16 | flex-direction: column; 17 | 18 | .Downloads__clear-button { 19 | width: fit-content; 20 | align-self: flex-end; 21 | margin-bottom: 30px; 22 | } 23 | 24 | .Downloads__search { 25 | margin-bottom: 30px; 26 | border-radius: 3px; 27 | height: 30px; 28 | font-size: 15px; 29 | padding-left: 20px; 30 | padding-right: 20px; 31 | } 32 | 33 | .Downloads__item { 34 | background-color: var(--color-background-primary); 35 | padding: 20px; 36 | border-radius: 3px; 37 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 38 | margin-bottom: 30px; 39 | cursor: pointer; 40 | display: flex; 41 | flex-direction: row; 42 | overflow: hidden; 43 | 44 | .Downloads__item-text { 45 | flex-grow: 1; 46 | overflow: hidden; 47 | 48 | .Downloads__item-title { 49 | font-weight: bold; 50 | } 51 | } 52 | 53 | .Downloads__item-control { 54 | padding: 10px; 55 | } 56 | 57 | .Downloads__item-control:hover { 58 | color: var(--color-icon-primary); 59 | } 60 | } 61 | 62 | .Downloads__item:hover{ 63 | border: 1px solid var(--color-border); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/renderer/App/components/DownloadsPreview/DownloadsPreview.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 2 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 3 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 4 | /* eslint-disable import/prefer-default-export */ 5 | import { Button } from '@mui/material'; 6 | import LinearProgress from '@mui/material/LinearProgress'; 7 | import { useTranslation } from 'react-i18next'; 8 | 9 | import { useAppSelector, useAppDispatch } from 'renderer/App/store/hooks'; 10 | 11 | import { 12 | DownloadItem, 13 | DownloadsState, 14 | clearDownloads, 15 | } from 'renderer/App/store/reducers/Downloads'; 16 | 17 | import './style.scss'; 18 | 19 | export const DownloadsPreview = () => { 20 | const { t } = useTranslation(); 21 | const { items }: DownloadsState = useAppSelector((state) => state.downloads); 22 | const dispatch = useAppDispatch(); 23 | 24 | const handleOnClick = (i: DownloadItem) => { 25 | window.app.tools.showItemInFolder(i.savePath); 26 | }; 27 | 28 | const handleClear = () => { 29 | dispatch(clearDownloads()); 30 | window.app.download.hideDownloadsPreview(); 31 | }; 32 | 33 | return ( 34 |
35 |
    36 | {items.map((i) => { 37 | return ( 38 |
  • handleOnClick(i)} 40 | key={`${i.etag}::${i.startTime}`} 41 | > 42 |
    {i.filename}
    43 | {i.state !== 'progressing' && i.state !== 'completed' && ( 44 |
    45 | {i.state ? t(i.state) : ''} 46 |
    47 | )} 48 | {i.state !== 'completed' && ( 49 | 54 | )} 55 |
  • 56 | ); 57 | })} 58 |
59 |
60 | 61 |
62 |
63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /src/renderer/App/components/DownloadsPreview/Types.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/src/renderer/App/components/DownloadsPreview/Types.d.ts -------------------------------------------------------------------------------- /src/renderer/App/components/DownloadsPreview/index.tsx: -------------------------------------------------------------------------------- 1 | import { DownloadsPreview } from './DownloadsPreview'; 2 | 3 | export * from './DownloadsPreview'; 4 | export default DownloadsPreview; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/DownloadsPreview/style.scss: -------------------------------------------------------------------------------- 1 | #DownloadsPreview__container { 2 | position: fixed; 3 | right: 0; 4 | top: 0; 5 | background-color: var(--color-background-tertiary); 6 | min-width: 255px; 7 | max-width: 400px; 8 | color: var(--color-text-primary); 9 | z-index: 100; 10 | height: auto; 11 | 12 | ul { 13 | list-style: none; 14 | padding: 0; 15 | margin: 0; 16 | 17 | li { 18 | padding: 20px; 19 | border-top: 1px solid var(--color-text-primary); 20 | cursor: pointer; 21 | 22 | .DownloadsPreview__item-progress { 23 | margin-top: 10px; 24 | } 25 | 26 | .DownloadsPreview__item-state { 27 | font-style: italic; 28 | } 29 | } 30 | 31 | li:hover { 32 | background-color: var(--color-background-5); 33 | } 34 | } 35 | 36 | .DownloadsPreview__clear { 37 | display: flex; 38 | margin-bottom: 10px; 39 | margin-top: 10px; 40 | justify-content: center; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/renderer/App/components/ErrorFallback/index.tsx: -------------------------------------------------------------------------------- 1 | import ErrorFallback from './ErrorFallback'; 2 | 3 | export default ErrorFallback; 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/ErrorFallback/style.scss: -------------------------------------------------------------------------------- 1 | .About__property-line { 2 | margin-bottom: 5px;; 3 | 4 | .About__property { 5 | font-weight: bold; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/App/components/Extensions/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface ExtensionsProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Extensions/index.ts: -------------------------------------------------------------------------------- 1 | import { Extensions } from './Extensions'; 2 | 3 | export * from './Extensions'; 4 | export default Extensions; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Extensions/style.scss: -------------------------------------------------------------------------------- 1 | #Extensions__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | overflow-y: auto; 12 | 13 | #Extensions__centered-container { 14 | display: flex; 15 | width: 800px; 16 | flex-direction: column; 17 | 18 | .Extensions__clear-button { 19 | width: fit-content; 20 | align-self: flex-end; 21 | margin-bottom: 30px; 22 | } 23 | 24 | .Extensions__search { 25 | margin-bottom: 30px; 26 | border-radius: 3px; 27 | height: 30px; 28 | font-size: 15px; 29 | padding-left: 20px; 30 | padding-right: 20px; 31 | } 32 | 33 | .Extensions__item { 34 | background-color: var(--color-background-primary); 35 | padding: 20px; 36 | border-radius: 3px; 37 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 38 | margin-bottom: 30px; 39 | cursor: pointer; 40 | display: flex; 41 | flex-direction: row; 42 | overflow: hidden; 43 | 44 | .Extensions__item-text { 45 | flex-grow: 1; 46 | overflow: hidden; 47 | 48 | .Extensions__item-name { 49 | font-weight: bold; 50 | } 51 | 52 | .Extensions__item-description { 53 | margin-top: 10px; 54 | } 55 | } 56 | 57 | .Extensions__item-control { 58 | padding: 10px; 59 | } 60 | 61 | .Extensions__item-control:hover { 62 | color: var(--color-icon-primary); 63 | } 64 | } 65 | 66 | .Extensions__item:hover{ 67 | border: 1px solid var(--color-border); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/renderer/App/components/History/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface HistoryProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/History/index.ts: -------------------------------------------------------------------------------- 1 | import { History } from './History'; 2 | 3 | export * from './History'; 4 | export default History; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/History/style.scss: -------------------------------------------------------------------------------- 1 | #History__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | overflow-y: auto; 12 | 13 | #History__centered-container { 14 | display: flex; 15 | width: 800px; 16 | flex-direction: column; 17 | flex-grow: 1; 18 | 19 | .History__clear-button { 20 | width: fit-content; 21 | align-self: flex-end; 22 | margin-bottom: 30px; 23 | } 24 | 25 | .History__search { 26 | margin-bottom: 30px; 27 | border-radius: 3px; 28 | height: 30px; 29 | font-size: 15px; 30 | padding-left: 20px; 31 | padding-right: 20px; 32 | } 33 | 34 | .History__item { 35 | background-color: var(--color-background-primary); 36 | padding: 20px; 37 | border-radius: 3px; 38 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 39 | margin-bottom: 30px; 40 | cursor: pointer; 41 | display: flex; 42 | flex-direction: row; 43 | overflow: hidden; 44 | height: 60px; 45 | 46 | .History__item-text { 47 | flex-grow: 1; 48 | overflow: scroll; 49 | 50 | .History__item-title { 51 | font-weight: bold; 52 | } 53 | } 54 | 55 | .History__item-control { 56 | padding: 10px; 57 | } 58 | 59 | .History__item-control:hover { 60 | color: var(--color-icon-primary); 61 | } 62 | } 63 | 64 | .History__item:hover{ 65 | border: 1px solid var(--color-border); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/renderer/App/components/LeftBar/assets.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' { 2 | const content: string; 3 | export default content; 4 | } 5 | 6 | declare module '*.svg' { 7 | const content: string; 8 | export default content; 9 | } 10 | -------------------------------------------------------------------------------- /src/renderer/App/components/LeftBar/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BonBon-exchange/bonbon-browser/42936adca95a86b1c302d83253a469e5a64fab7d/src/renderer/App/components/LeftBar/icon.png -------------------------------------------------------------------------------- /src/renderer/App/components/LeftBar/index.tsx: -------------------------------------------------------------------------------- 1 | import { LeftBar } from './LeftBar'; 2 | 3 | export * from './LeftBar'; 4 | export default LeftBar; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/LeftBar/style.scss: -------------------------------------------------------------------------------- 1 | #LeftBar__browserFavContainer { 2 | position: fixed; 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | width: 50px; 7 | align-items: center; 8 | z-index: 10; 9 | background-color: var(--color-background-tertiary); 10 | padding-top: 10px; 11 | 12 | .LeftBar__browserContainer:hover { 13 | .LeftBar__closeBrowser, 14 | .LeftBar__maximizeBrowser { 15 | display: block; 16 | } 17 | 18 | .LeftBar__browserFav { 19 | cursor: pointer; 20 | background-color: var(--color-background-5); 21 | } 22 | } 23 | 24 | ul { 25 | list-style: none; 26 | padding: 0; 27 | } 28 | 29 | .LeftBar__closeBrowser, 30 | .LeftBar__maximizeBrowser { 31 | position: relative; 32 | display: none; 33 | transform: translate(26px, 11px); 34 | color: var(--color-icon-primary); 35 | margin-top: -19px; 36 | cursor: pointer; 37 | } 38 | } 39 | 40 | .LeftBar__closeBrowser { 41 | svg { 42 | height: 15px; 43 | } 44 | } 45 | 46 | .LeftBar__maximizeBrowser { 47 | top: 3px; 48 | right: 3px; 49 | pointer-events: none; 50 | 51 | svg { 52 | height: 12px; 53 | } 54 | } 55 | 56 | .LeftBar__browserFav { 57 | cursor: pointer; 58 | padding: 7px; 59 | margin-bottom: 4px; 60 | border-radius: 3px; 61 | 62 | &.selected { 63 | background-color: var(--color-background-5); 64 | } 65 | } 66 | 67 | .LeftBar__browserFav:hover { 68 | cursor: pointer; 69 | background-color: var(--color-background-5); 70 | } 71 | 72 | .LeftBar__browserFavImg { 73 | width: 24px; 74 | height: 24px; 75 | pointer-events: none; 76 | cursor: pointer; 77 | } 78 | 79 | .LeftBar__maximizeBrowser ~ .LeftBar__browserFav img { 80 | filter: grayscale(70%); 81 | opacity: 0.3; 82 | } 83 | 84 | #LeftBar__magic-chat-icon { 85 | bottom: 20px; 86 | position: absolute; 87 | color: var(--color-text-primary); 88 | cursor: pointer; 89 | } 90 | -------------------------------------------------------------------------------- /src/renderer/App/components/Loader/Loader.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/alt-text */ 2 | /* eslint-disable import/prefer-default-export */ 3 | import loadingImg from 'renderer/App/svg/loading.svg'; 4 | 5 | import './style.scss'; 6 | 7 | export const Loader = () => { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/renderer/App/components/Loader/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Loader'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Loader/style.scss: -------------------------------------------------------------------------------- 1 | .Loader__loading { 2 | display: flex; 3 | flex-grow: 1; 4 | justify-content: center; 5 | align-items: center; 6 | } 7 | -------------------------------------------------------------------------------- /src/renderer/App/components/MacOSControls/MacOSControls.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | /* eslint-disable import/prefer-default-export */ 4 | 5 | import './style.scss'; 6 | 7 | export const MacOSControls = ({ 8 | closeBrowser, 9 | toggleFullSizeBrowser, 10 | minimizeBrowser, 11 | isPinned 12 | }: { 13 | closeBrowser: () => void 14 | toggleFullSizeBrowser: () => void 15 | minimizeBrowser: () => void 16 | isPinned: boolean 17 | }) => { 18 | return ( 19 |
20 | {!isPinned && } 21 | 22 | 23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/renderer/App/components/MacOSControls/index.tsx: -------------------------------------------------------------------------------- 1 | import { MacOSControls } from './MacOSControls'; 2 | 3 | export default MacOSControls; 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/MacOSControls/style.scss: -------------------------------------------------------------------------------- 1 | .window-controls { 2 | display: flex; 3 | gap: 8px; /* Espace entre les boutons */ 4 | padding: 10px; /* Optionnel, pour le padding autour des boutons */ 5 | cursor: auto; 6 | } 7 | 8 | .control { 9 | width: 12px; /* Diamètre des boutons */ 10 | height: 12px; 11 | border-radius: 50%; /* Pour faire des cercles */ 12 | display: inline-block; 13 | position: relative; 14 | } 15 | 16 | .red { 17 | background-color: #ff5f57; /* Couleur rouge */ 18 | } 19 | 20 | .red::before { 21 | content: '✕'; /* Symbole X */ 22 | font-size: 10px; 23 | color: black; 24 | display: none; 25 | position: absolute; 26 | top: 50%; 27 | left: 50%; 28 | transform: translate(-50%, -50%); 29 | } 30 | 31 | .red:hover::before { 32 | display: block; 33 | } 34 | 35 | .yellow { 36 | background-color: #ffbd2e; /* Couleur jaune */ 37 | } 38 | 39 | .yellow::before { 40 | content: '–'; /* Symbole tiret */ 41 | font-size: 14px; 42 | color: black; 43 | display: none; 44 | position: absolute; 45 | top: 50%; 46 | left: 50%; 47 | transform: translate(-50%, -50%); 48 | } 49 | 50 | .yellow:hover::before { 51 | display: block; 52 | } 53 | 54 | .green { 55 | background-color: #28c840; /* Couleur verte */ 56 | } 57 | 58 | .green::before { 59 | content: '⧉'; /* Symbole carré avec ligne oblique (vous pouvez changer ce symbole selon le design exact) */ 60 | font-size: 10px; 61 | color: black; 62 | display: none; 63 | position: absolute; 64 | top: 50%; 65 | left: 50%; 66 | transform: translate(-50%, -50%); 67 | } 68 | 69 | .green:hover::before { 70 | display: block; 71 | } 72 | -------------------------------------------------------------------------------- /src/renderer/App/components/Minimap/Types.d.ts: -------------------------------------------------------------------------------- 1 | export type MiniWindow = { 2 | left: number; 3 | top: number; 4 | width: number; 5 | height: number; 6 | favicon: string | undefined; 7 | id: string; 8 | isLoading: boolean; 9 | capture?: string; 10 | } 11 | 12 | export type MiniView = { 13 | top: number; 14 | height: number; 15 | } 16 | 17 | export interface MinimapProps { 18 | handleHide: () => void; 19 | } 20 | -------------------------------------------------------------------------------- /src/renderer/App/components/Minimap/index.ts: -------------------------------------------------------------------------------- 1 | import { Minimap } from './Minimap'; 2 | 3 | export * from './Minimap'; 4 | export default Minimap; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Minimap/style.scss: -------------------------------------------------------------------------------- 1 | #Minimap__container { 2 | position: fixed; 3 | display: flex; 4 | right: 0; 5 | width: 185px; 6 | height: 100%; 7 | user-select: none; 8 | z-index: 12; 9 | 10 | #Minimap__view { 11 | background: white; 12 | opacity: 0.2; 13 | width: 100%; 14 | position: absolute; 15 | user-select: none; 16 | z-index: 2; 17 | } 18 | 19 | .Minimap__window { 20 | background-color: var(--color-background-tertiary); 21 | position: absolute; 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | user-select: none; 26 | 27 | img { 28 | user-select: none; 29 | max-width: 100%; 30 | max-height: 100%; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/renderer/App/components/Notification/Notification.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 2 | /* eslint-disable promise/always-return */ 3 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 4 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 5 | /* eslint-disable import/prefer-default-export */ 6 | import { useEffect } from 'react'; 7 | 8 | import { CloseButton } from 'renderer/App/components/CloseButton'; 9 | 10 | import { NotificationProps } from './Types'; 11 | 12 | import './style.scss'; 13 | 14 | export const Notification = ({ children, closePopup, className }: NotificationProps) => { 15 | 16 | useEffect(() => { 17 | setTimeout(() => { 18 | closePopup() 19 | }, 1500) 20 | }, [closePopup]) 21 | 22 | return ( 23 |
24 | 25 | {children} 26 |
27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /src/renderer/App/components/Notification/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface NotificationProps { 2 | children?: JSX.Element; 3 | className?: string; 4 | closePopup: () => void; 5 | } 6 | -------------------------------------------------------------------------------- /src/renderer/App/components/Notification/index.tsx: -------------------------------------------------------------------------------- 1 | import { Notification } from './Notification'; 2 | 3 | export * from './Notification'; 4 | export default Notification; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Notification/style.scss: -------------------------------------------------------------------------------- 1 | #Notification__container { 2 | z-index: 2000; 3 | height: auto; 4 | width: 300px; 5 | top: -220px; 6 | right: 20px; 7 | background-color: var(--color-background-tertiary); 8 | color: var(--color-text-primary); 9 | border-radius: 5px; 10 | border: 1px solid var(--color-border); 11 | display: flex; 12 | padding: 20px; 13 | position: fixed !important; 14 | flex-direction: column; 15 | transition: top 0.5s ease-in-out; 16 | 17 | &.display { 18 | top: 20px; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/renderer/App/components/Ping/Ping.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | import { useAnalytics } from 'renderer/App/hooks/useAnalytics'; 4 | 5 | export const Ping = () => { 6 | const { anal } = useAnalytics(); 7 | useEffect(() => { 8 | anal.logEvent('ping'); 9 | setInterval(() => { 10 | anal.logEvent('ping'); 11 | }, 60000); 12 | }, [anal]); 13 | 14 | return
; 15 | }; 16 | -------------------------------------------------------------------------------- /src/renderer/App/components/Ping/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Ping'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/PopoverColorPicker/PopoverColorPicker.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useRef, useState } from 'react'; 2 | import { HexColorPicker } from 'react-colorful'; 3 | 4 | import useClickOutside from 'renderer/App/hooks/useClickOutside'; 5 | 6 | import './styles.scss'; 7 | 8 | export const PopoverColorPicker = ({ 9 | color, 10 | onChange, 11 | }: { 12 | color: string; 13 | onChange: (newColor: string) => void; 14 | }) => { 15 | const popover = useRef(null); 16 | const [isOpen, toggle] = useState(false); 17 | 18 | const close = useCallback(() => toggle(false), []); 19 | useClickOutside(popover, close); 20 | 21 | return ( 22 |
23 |
toggle(true)} 27 | /> 28 | 29 | {isOpen && ( 30 |
31 | 32 |
33 | )} 34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/renderer/App/components/PopoverColorPicker/index.tsx: -------------------------------------------------------------------------------- 1 | import { PopoverColorPicker } from './PopoverColorPicker'; 2 | 3 | export default PopoverColorPicker; 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/PopoverColorPicker/styles.scss: -------------------------------------------------------------------------------- 1 | .swatch { 2 | width: 20px; 3 | height: 20px; 4 | } 5 | 6 | .color-picker { 7 | margin-right: 10px; 8 | } 9 | 10 | .picker { 11 | position: absolute; 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/App/components/Popup/Popup.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 2 | /* eslint-disable promise/always-return */ 3 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 4 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 5 | /* eslint-disable import/prefer-default-export */ 6 | 7 | import React from 'react'; 8 | import { CloseButton } from 'renderer/App/components/CloseButton'; 9 | 10 | import { PopupProps } from './Types'; 11 | 12 | import './style.scss'; 13 | 14 | export const Popup = ({ children, closePopup, title }: PopupProps) => { 15 | const handleBackgroundClick = ( 16 | e: React.MouseEvent 17 | ) => { 18 | if (e.target === e.currentTarget) { 19 | closePopup(); 20 | } 21 | }; 22 | return ( 23 |
24 | 30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/renderer/App/components/Popup/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface PopupProps { 2 | children?: JSX.Element; 3 | closePopup: () => void; 4 | title: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/renderer/App/components/Popup/index.tsx: -------------------------------------------------------------------------------- 1 | import { Popup } from './Popup'; 2 | 3 | export * from './Popup'; 4 | export default Popup; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Popup/style.scss: -------------------------------------------------------------------------------- 1 | #Popup__container { 2 | z-index: 2000; 3 | height: auto; 4 | width: 300px; 5 | top: calc(50% - 150px) !important; 6 | left: calc(50% - 150px) !important; 7 | background-color: var(--color-background-tertiary); 8 | color: var(--color-text-primary); 9 | border-radius: 5px; 10 | border: 1px solid var(--color-border); 11 | display: flex; 12 | padding: 20px; 13 | position: fixed !important; 14 | flex-direction: column; 15 | } 16 | 17 | #Popup__title { 18 | font-size: 18px; 19 | font-weight: bold; 20 | margin-bottom: 20px; 21 | } 22 | 23 | .Popup__overlay { 24 | position: fixed; 25 | top: 0; 26 | left: 0; 27 | width: 100%; 28 | height: 100%; 29 | display: flex; 30 | justify-content: center; 31 | align-items: center; 32 | z-index: 1001; 33 | } 34 | -------------------------------------------------------------------------------- /src/renderer/App/components/SearchForm/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface SearchFormProps { 2 | browserId: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/SearchForm/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SearchForm'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/SearchForm/style.scss: -------------------------------------------------------------------------------- 1 | .SearchForm__container { 2 | position: absolute; 3 | right: 19px; 4 | top: 50px; 5 | height: 50px; 6 | width: 350px; 7 | background-color: var(--color-background-primary); 8 | border-radius: 3px; 9 | z-index: 100; 10 | color: var(--color-text-primary); 11 | display: flex; 12 | flex-direction: row; 13 | border: 1px solid var(--color-border); 14 | align-items: center; 15 | justify-content: space-around; 16 | 17 | .MuiTextField-root { 18 | input { 19 | background-color: transparent !important; 20 | } 21 | } 22 | 23 | .MuiOutlinedInput-root { 24 | height: 50px; 25 | color: var(--color-text-primary) !important; 26 | 27 | fieldset { 28 | outline: none !important; 29 | border: none !important; 30 | } 31 | } 32 | 33 | .SearchForm__control-item { 34 | padding: 3px; 35 | border-radius: 3px; 36 | cursor: pointer; 37 | } 38 | 39 | .SearchForm__control-item:hover{ 40 | background-color: var(--color-background-5); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/ApplicationSettings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ApplicationSettings'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/ApplicationSettings/styles.scss: -------------------------------------------------------------------------------- 1 | .color-picker-input { 2 | margin-right: 30px !important; 3 | width: 55px; 4 | } 5 | 6 | .picker { 7 | margin-left: -20px; 8 | } 9 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/BrowsingSettings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BrowsingSettings'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/BrowsingSettings/style.scss: -------------------------------------------------------------------------------- 1 | #browsing-settings-default-webpage { 2 | width: 100%; 3 | } 4 | 5 | #browsing-settings-default-width, #browsing-settings-default-height { 6 | width: 50px; 7 | } 8 | 9 | .Settings__item-title { 10 | font-weight: bold; 11 | margin-bottom: 10px; 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/ExtensionsSettings/ExtensionsSettings.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable promise/catch-or-return */ 2 | /* eslint-disable jsx-a11y/label-has-associated-control */ 3 | /* eslint-disable import/prefer-default-export */ 4 | import { useEffect, useState } from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | 7 | import { useAppDispatch } from 'renderer/App/store/hooks'; 8 | import { setSetting } from 'renderer/App/store/reducers/Settings'; 9 | import { useSettings } from 'renderer/App/hooks/useSettings'; 10 | 11 | export const ExtensionsSettings = () => { 12 | const { t } = useTranslation(); 13 | const dispatch = useAppDispatch(); 14 | const settings = useSettings(); 15 | const [extForceUBlock, setForceUBlock] = useState( 16 | settings['extensions.forceInstallUBlockOrigin'] 17 | ); 18 | 19 | useEffect(() => { 20 | dispatch( 21 | setSetting({ 22 | key: 'extensions.forceInstallUBlockOrigin', 23 | value: extForceUBlock, 24 | }) 25 | ); 26 | }, [dispatch, extForceUBlock]); 27 | 28 | return ( 29 | <> 30 |

{t('Extensions')}

31 |
32 | setForceUBlock(e.target.checked)} 37 | /> 38 | 41 |
42 | {t( 43 | 'If checked, the app will automatically download uBlockOrigin and install it when starting.' 44 | )} 45 |
46 |
47 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/ExtensionsSettings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ExtensionsSettings'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/Settings.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | /* eslint-disable import/prefer-default-export */ 4 | import { useState, ReactElement } from 'react'; 5 | import { useTranslation } from 'react-i18next'; 6 | 7 | import { CloseButton } from 'renderer/App/components/CloseButton'; 8 | import { ApplicationSettings } from './ApplicationSettings'; 9 | import { BrowsingSettings } from './BrowsingSettings'; 10 | import { ExtensionsSettings } from './ExtensionsSettings'; 11 | 12 | import './style.scss'; 13 | import { SettingsProps } from './Types'; 14 | 15 | export const Settings = ({ handleClose }: SettingsProps) => { 16 | const { t } = useTranslation(); 17 | const [selectedView, setSelectedView] = useState( 18 | 19 | ); 20 | 21 | return ( 22 |
23 | 24 |
25 |

{t('Settings')}

26 |
    27 |
  • setSelectedView()}> 28 | {t('Application')} 29 |
  • 30 |
  • setSelectedView()} 32 | data-testid="settings-browsing-link" 33 | > 34 | {t('Browsing')} 35 |
  • 36 |
  • setSelectedView()} 38 | data-testid="settings-extensions-link" 39 | > 40 | {t('Extensions')} 41 |
  • 42 |
43 |
44 |
{selectedView}
45 |
46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/Types.d.ts: -------------------------------------------------------------------------------- 1 | export interface SettingsProps { 2 | handleClose: () => void; 3 | } 4 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/index.ts: -------------------------------------------------------------------------------- 1 | import { Settings } from './Settings'; 2 | 3 | export * from './Settings'; 4 | export default Settings; 5 | -------------------------------------------------------------------------------- /src/renderer/App/components/Settings/style.scss: -------------------------------------------------------------------------------- 1 | #Settings__container { 2 | background-color: var(--color-background-secondary); 3 | color: var(--color-text-primary); 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10; 8 | display: flex; 9 | flex-direction: row; 10 | 11 | .Settings__item { 12 | background-color: var(--color-background-primary); 13 | padding: 20px; 14 | border-radius: 3px; 15 | box-shadow: 0px 1px 3px var(--color-background-tertiary); 16 | margin-bottom: 30px; 17 | 18 | .flex-items { 19 | display: flex; 20 | } 21 | 22 | input[type='text'] { 23 | margin-right: 10px; 24 | } 25 | 26 | .Settings__item-description { 27 | font-style: italic; 28 | margin-top: 10px; 29 | } 30 | } 31 | 32 | #Settings__left-panel { 33 | width: 300px; 34 | height: 100%; 35 | border-right: 1px solid var(--color-border); 36 | padding: 50px; 37 | 38 | ul { 39 | list-style: none; 40 | margin: 0; 41 | padding: 0; 42 | 43 | li { 44 | cursor: pointer; 45 | padding: 5px; 46 | border-radius: 3px; 47 | } 48 | 49 | li:hover { 50 | background-color: var(--color-background-primary); 51 | } 52 | } 53 | } 54 | 55 | #Settings__right-panel { 56 | flex-grow: 1; 57 | height: 100%; 58 | padding: 50px; 59 | overflow: scroll; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/renderer/App/components/SettingsStateSynced/SettingsStateSynced.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useMessaging } from 'renderer/App/hooks/useMessaging'; 3 | 4 | export const SettingsStateSynced = () => { 5 | const { connectedUsers } = useMessaging(); 6 | useEffect(() => { 7 | console.log({ connectedUsers }); 8 | }, [connectedUsers]); 9 | 10 | return
; 11 | }; 12 | -------------------------------------------------------------------------------- /src/renderer/App/components/SettingsStateSynced/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './SettingsStateSynced'; 2 | -------------------------------------------------------------------------------- /src/renderer/App/components/Unmaximize/Unmaximize.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export const Unmaximize = () => { 3 | return ( 4 | 11 | 12 | 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/renderer/App/components/Unmaximize/index.ts: -------------------------------------------------------------------------------- 1 | import { Unmaximize } from './Unmaximize'; 2 | 3 | export default Unmaximize; 4 | -------------------------------------------------------------------------------- /src/renderer/App/firebase.tsx: -------------------------------------------------------------------------------- 1 | // src/firebase.ts 2 | 3 | import { initializeApp } from 'firebase/app'; 4 | import { getDatabase } from 'firebase/database'; 5 | import { getAuth, signInAnonymously } from 'firebase/auth'; 6 | import { getAnalytics, logEvent } from 'firebase/analytics'; 7 | 8 | // Your Firebase configuration (replace with your actual config) 9 | const firebaseConfig = { 10 | apiKey: 'AIzaSyBZ0V6V43EW3NHE-z0fZZnlpe22mE55uqQ', // Replace with your API key 11 | authDomain: 'bonbon-browser.firebaseapp.com', // Replace with your Auth domain 12 | databaseURL: 13 | 'https://bonbon-browser-default-rtdb.europe-west1.firebasedatabase.app', // Replace with your Database URL 14 | projectId: 'bonbon-browser', // Replace with your Project ID 15 | storageBucket: 'bonbon-browser.appspot.com', // Replace with your Storage Bucket 16 | messagingSenderId: '313574192651', // Replace with your Messaging Sender ID 17 | appId: '1:313574192651:web:cbe8cd4ce4797d7c82f90e', // Replace with your App ID 18 | measurementId: 'G-GVME8GLMGZ', 19 | }; 20 | 21 | // Initialize Firebase 22 | const app = initializeApp(firebaseConfig); 23 | 24 | // Initialize Realtime Database and Auth 25 | const database = getDatabase(app); 26 | const auth = getAuth(app); 27 | 28 | const analytics = getAnalytics(); 29 | 30 | // Sign in anonymously 31 | signInAnonymously(auth) 32 | .then(() => { 33 | console.log('Signed in anonymously to Firebase Auth'); 34 | logEvent(analytics, 'firebase_signin_anonymous'); 35 | }) 36 | .catch((error) => { 37 | console.error('Firebase Auth Error:', error); 38 | logEvent(analytics, 'firebase_signin_anonymous_error'); 39 | }); 40 | 41 | export { database, analytics }; 42 | -------------------------------------------------------------------------------- /src/renderer/App/helpers/d2.ts: -------------------------------------------------------------------------------- 1 | import { BoardType } from 'renderer/App/components/Board/Types'; 2 | 3 | /* eslint-disable import/prefer-default-export */ 4 | export const overlaps = ( 5 | x: number, 6 | y: number, 7 | height: number, 8 | width: number, 9 | rect2: { x: number; y: number; width: number; height: number } 10 | ) => { 11 | const isInHorizontalBounds = x < rect2.x + rect2.width && x + width > rect2.x; 12 | const isInVerticalBounds = y < rect2.y + rect2.height && y + height > rect2.y; 13 | const isOverlapping = isInHorizontalBounds && isInVerticalBounds; 14 | return isOverlapping; 15 | }; 16 | 17 | export const getCoordinateWithNoCollision = ( 18 | document: Document, 19 | board: BoardType, 20 | height: number, 21 | width: number 22 | ): { x: number; y: number } => { 23 | let x = 0; 24 | let y = 0; 25 | const step = 10; 26 | const maxX = 27 | (document.querySelector('#Board__container')?.clientWidth || 0) - 28 | width - 29 | step; 30 | 31 | if ( 32 | width + step > 33 | (document.querySelector('#Board__container')?.clientWidth || 0) 34 | ) { 35 | return { x: 10, y: 10 }; 36 | } 37 | 38 | let collide = true; 39 | while (collide) { 40 | y += step; 41 | while (collide && x < maxX) { 42 | x += step; 43 | collide = !board.browsers.every( 44 | (b) => 45 | overlaps(x, y, height, width, { 46 | x: b.left, 47 | y: b.top, 48 | width: b.width, 49 | height: b.height, 50 | }) === false 51 | ); 52 | } 53 | if (collide) x = 0; 54 | } 55 | 56 | return { x, y }; 57 | }; 58 | -------------------------------------------------------------------------------- /src/renderer/App/helpers/dom.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { Electron } from 'namespaces/_electronist'; 3 | 4 | export const getContainerFromBrowserId = (browserId: string) => { 5 | const container = document.querySelector(`#Browser__${browserId}`); 6 | return container; 7 | }; 8 | 9 | export const getWebviewFromBrowserId = (browserId: string) => { 10 | const container = getContainerFromBrowserId(browserId); 11 | const webview: Electron.WebviewTag | undefined | null = 12 | container?.querySelector('webview'); 13 | 14 | return webview; 15 | }; 16 | -------------------------------------------------------------------------------- /src/renderer/App/helpers/web.ts: -------------------------------------------------------------------------------- 1 | import { HistoryItem } from '../components/BrowserInputSuggestions/Types'; 2 | 3 | /* eslint-disable import/prefer-default-export */ 4 | export const isValidHttpUrl = (s: string) => { 5 | let url; 6 | 7 | try { 8 | url = new URL(s); 9 | } catch (_) { 10 | return false; 11 | } 12 | 13 | return url.protocol === 'http:' || url.protocol === 'https:'; 14 | }; 15 | 16 | export const makeSearchUrl = (search: string, searchEngine: string): string => { 17 | switch (searchEngine) { 18 | case 'presearch': 19 | return `https://presearch.com/search?q=${search}`; 20 | 21 | case 'qwant': 22 | return `https://www.qwant.com/?l=fr&q=${search}&t=web`; 23 | 24 | case 'duckduckgo': 25 | return `https://duckduckgo.com/?q=${search}`; 26 | 27 | case 'yandex': 28 | return `https://yandex.com/search/?text=${search}`; 29 | 30 | case 'swisscows': 31 | return `https://swisscows.com/web?query=${search}`; 32 | 33 | case 'ecosia': 34 | return `https://www.ecosia.org/search?method=index&q=${search}`; 35 | 36 | case 'startpage': 37 | return `https://www.startpage.com/do/search?query=${search}`; 38 | 39 | case 'google': 40 | default: 41 | return `https://www.google.com/search?q=${search}`; 42 | } 43 | }; 44 | 45 | export const getDomainsFromHistory = (items: HistoryItem[]) => { 46 | const objects = items.map((i) => { 47 | const url = new URL(i.url); 48 | return { 49 | ...i, 50 | url: url.origin, 51 | }; 52 | }); 53 | 54 | return objects.reduce((acc, val) => { 55 | const exists = acc.find((a) => a.url === val.url); 56 | return exists ? acc : [val, ...acc]; 57 | }, [] as HistoryItem[]); 58 | }; 59 | 60 | export const getHostsFromHistory = (items: HistoryItem[]) => { 61 | const objects = items.map((i) => { 62 | try { 63 | const url = new URL(i.url); 64 | return { 65 | ...i, 66 | url: url.host.indexOf('www.') === 0 ? url.host.substring(4) : url.host, 67 | }; 68 | } catch (e) { 69 | return i; 70 | } 71 | }); 72 | 73 | return objects.reduce((acc, val) => { 74 | const exists = acc.find((a) => a.url === val.url); 75 | return exists ? acc : [val, ...acc]; 76 | }, [] as HistoryItem[]); 77 | }; 78 | -------------------------------------------------------------------------------- /src/renderer/App/hooks/useAnalytics.tsx: -------------------------------------------------------------------------------- 1 | import { logEvent } from 'firebase/analytics'; 2 | import { analytics } from '../firebase'; 3 | 4 | export const useAnalytics = () => { 5 | const anal = { 6 | logEvent: (name: string, params?: Record) => { 7 | logEvent(analytics, name, params); 8 | }, 9 | }; 10 | 11 | return { 12 | anal, 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /src/renderer/App/hooks/useBoard.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { useAppSelector } from 'renderer/App/store/hooks'; 3 | import { BoardType } from 'renderer/App/components/Board/Types'; 4 | 5 | export const useBoard = () => { 6 | const { board }: { board: BoardType } = useAppSelector( 7 | (state) => state.board 8 | ); 9 | 10 | return board; 11 | }; 12 | -------------------------------------------------------------------------------- /src/renderer/App/hooks/useClickOutside.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | // Improved version of https://usehooks.com/useOnClickOutside/ 4 | const useClickOutside = (ref: any, handler: (e: Event) => unknown) => { 5 | useEffect(() => { 6 | let startedInside = false; 7 | let startedWhenMounted = false; 8 | 9 | const listener = (event: Event) => { 10 | // Do nothing if `mousedown` or `touchstart` started inside ref element 11 | if (startedInside || !startedWhenMounted) return; 12 | // Do nothing if clicking ref's element or descendent elements 13 | if (!ref.current || ref.current.contains(event.target)) return; 14 | 15 | handler(event); 16 | }; 17 | 18 | const validateEventStart = (event: Event) => { 19 | startedWhenMounted = ref.current; 20 | startedInside = ref.current && ref.current.contains(event.target); 21 | }; 22 | 23 | document.addEventListener('mousedown', validateEventStart); 24 | document.addEventListener('touchstart', validateEventStart); 25 | document.addEventListener('click', listener); 26 | 27 | return () => { 28 | document.removeEventListener('mousedown', validateEventStart); 29 | document.removeEventListener('touchstart', validateEventStart); 30 | document.removeEventListener('click', listener); 31 | }; 32 | }, [ref, handler]); 33 | }; 34 | 35 | export default useClickOutside; 36 | -------------------------------------------------------------------------------- /src/renderer/App/hooks/useSettings.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { useAppSelector } from 'renderer/App/store/hooks'; 3 | 4 | export const useSettings = () => { 5 | const { settings }: { settings: Record } = useAppSelector( 6 | (state) => state.settings 7 | ); 8 | 9 | return settings; 10 | }; 11 | -------------------------------------------------------------------------------- /src/renderer/App/i18n-resources.d.ts: -------------------------------------------------------------------------------- 1 | import 'react-i18next' 2 | 3 | declare module 'react-i18next' { 4 | export interface Resources { 5 | translation: typeof import('./locales/en.json') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/App/i18n.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | import { initReactI18next } from 'react-i18next'; 3 | import LanguageDetector from 'i18next-browser-languagedetector'; 4 | 5 | import en from 'renderer/App/locales/en.json'; 6 | import es from 'renderer/App/locales/es.json'; 7 | import fr from 'renderer/App/locales/fr.json'; 8 | import nl from 'renderer/App/locales/nl.json'; 9 | import pl from 'renderer/App/locales/pl.json'; 10 | import ru from 'renderer/App/locales/ru.json'; 11 | import ar from 'renderer/App/locales/ar.json'; 12 | import cn from 'renderer/App/locales/cn.json'; 13 | import pt from 'renderer/App/locales/pt.json'; 14 | import tr from 'renderer/App/locales/tr.json'; 15 | import de from 'renderer/App/locales/de.json'; 16 | import fa from 'renderer/App/locales/fa.json'; 17 | import ja from 'renderer/App/locales/ja.json'; 18 | import bn from 'renderer/App/locales/bn.json'; 19 | import hi from 'renderer/App/locales/hi.json'; 20 | 21 | const resources = { 22 | en: { 23 | translation: en, 24 | }, 25 | es: { 26 | translation: es, 27 | }, 28 | fr: { 29 | translation: fr, 30 | }, 31 | nl: { 32 | translation: nl, 33 | }, 34 | pl: { 35 | translation: pl, 36 | }, 37 | ru: { 38 | translation: ru, 39 | }, 40 | ar: { 41 | translation: ar, 42 | }, 43 | cn: { 44 | translation: cn, 45 | }, 46 | pt: { 47 | translation: pt, 48 | }, 49 | tr: { 50 | translation: tr, 51 | }, 52 | de: { 53 | translation: de, 54 | }, 55 | fa: { 56 | translation: fa, 57 | }, 58 | ja: { 59 | translation: ja, 60 | }, 61 | bn: { 62 | translation: bn, 63 | }, 64 | hi: { 65 | translation: hi, 66 | }, 67 | }; 68 | 69 | i18n 70 | .use(LanguageDetector) 71 | .use(initReactI18next) 72 | .init({ 73 | resources, 74 | interpolation: { 75 | escapeValue: false, 76 | }, 77 | }); 78 | 79 | export default i18n; 80 | -------------------------------------------------------------------------------- /src/renderer/App/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | 3 | import { App } from './App'; 4 | 5 | const container = document.getElementById('root')!; 6 | const root = createRoot(container); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /src/renderer/App/store/hooks.ts: -------------------------------------------------------------------------------- 1 | import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; 2 | import type { RootState, AppDispatch } from './store'; 3 | 4 | export const useAppDispatch = () => useDispatch(); 5 | export const useAppSelector: TypedUseSelectorHook = useSelector; 6 | -------------------------------------------------------------------------------- /src/renderer/App/store/migrations.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export const migrations = { 3 | 2: (state: any) => ({ ...state, board: { ...state.board, closedUrls: [] } }), 4 | 3: (state: any) => ({ 5 | ...state, 6 | board: { ...state.board, browsersActivity: [] }, 7 | }), 8 | }; 9 | -------------------------------------------------------------------------------- /src/renderer/App/store/reducers/Downloads.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { DownloadState } from 'renderer/TitleBar/components/TopBar/Types'; 3 | 4 | export type DownloadItem = { 5 | savePath: string; 6 | filename: string; 7 | progress: number; 8 | etag: string; 9 | startTime: number; 10 | state: DownloadState; 11 | }; 12 | 13 | export type DownloadsState = { 14 | items: DownloadItem[]; 15 | }; 16 | 17 | export const initialState: DownloadsState = { 18 | items: [], 19 | }; 20 | 21 | export const downloadsSlice = createSlice({ 22 | name: 'downloads', 23 | initialState, 24 | reducers: { 25 | setDownloadItem: (state, action: PayloadAction) => { 26 | const itemIndex = state.items.findIndex( 27 | (i) => 28 | i.savePath === action.payload.savePath && 29 | i.startTime === action.payload.startTime 30 | ); 31 | if (itemIndex === -1) state.items.push(action.payload); 32 | else state.items[itemIndex] = action.payload; 33 | 34 | if (state.items.length > 10) state.items.splice(0, 1); 35 | }, 36 | clearDownloads: (state) => { 37 | state.items = []; 38 | }, 39 | }, 40 | }); 41 | 42 | export const { setDownloadItem, clearDownloads } = downloadsSlice.actions; 43 | 44 | export default downloadsSlice.reducer; 45 | -------------------------------------------------------------------------------- /src/renderer/App/store/reducers/Settings.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { flatten } from 'flat'; 3 | import { logEvent } from 'firebase/analytics'; 4 | 5 | import { analytics } from 'renderer/App/firebase'; 6 | 7 | export type SettingsState = { 8 | settings: { 9 | [key: string]: any; 10 | }; 11 | }; 12 | 13 | export const initialState: SettingsState = { 14 | settings: {}, 15 | }; 16 | 17 | // The settingsSlice for managing application settings 18 | export const settingsSlice = createSlice({ 19 | name: 'settings', 20 | initialState, 21 | reducers: { 22 | // Set a single setting value 23 | setSetting: (state, action: PayloadAction<{ key: string; value: any }>) => { 24 | state.settings[action.payload.key] = action.payload.value; 25 | window.app.config.set({ 26 | key: action.payload.key, 27 | value: action.payload.value, 28 | }); 29 | 30 | logEvent(analytics, 'setting_change', { 31 | [action.payload.key.replace('.', ':')]: action.payload.value, 32 | }); 33 | }, 34 | 35 | // Sync settings by replacing the whole state with new settings 36 | syncSettings: (state, action: PayloadAction>) => { 37 | state.settings = { ...state.settings, ...flatten(action.payload) }; // Merge new settings with existing state 38 | }, 39 | }, 40 | }); 41 | 42 | // Exporting the actions 43 | export const { setSetting, syncSettings } = settingsSlice.actions; 44 | 45 | // Exporting the reducer 46 | export default settingsSlice.reducer; 47 | -------------------------------------------------------------------------------- /src/renderer/App/store/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore, Store } from '@reduxjs/toolkit'; 2 | import { 3 | persistStore, 4 | persistReducer, 5 | createMigrate, 6 | FLUSH, 7 | REHYDRATE, 8 | PAUSE, 9 | PERSIST, 10 | PURGE, 11 | REGISTER, 12 | Persistor, 13 | } from 'redux-persist'; 14 | import storage from 'redux-persist/lib/storage'; 15 | 16 | import boardReducer from './reducers/Board'; 17 | import downloadsReducer from './reducers/Downloads'; 18 | import settingsReducer from './reducers/Settings'; 19 | import { migrations } from './migrations'; 20 | 21 | export const store: Store = configureStore({ 22 | reducer: { 23 | board: boardReducer, 24 | downloads: downloadsReducer, 25 | settings: settingsReducer, 26 | }, 27 | devTools: process.env.NODE_ENV !== 'production', 28 | }); 29 | 30 | export type RootState = ReturnType; 31 | export type AppDispatch = typeof store.dispatch; 32 | 33 | export const getPersistedStoreAndPersistor = ( 34 | id: string 35 | ): { persistor: Persistor; store: Store } => { 36 | const persistConfig = { 37 | key: id, 38 | version: 3, 39 | storage, 40 | migrate: createMigrate(migrations, { debug: true }), 41 | }; 42 | const persistedBoard = persistReducer(persistConfig, boardReducer); 43 | const persistedDownloads = persistReducer(persistConfig, downloadsReducer); 44 | const persistedSettings = persistReducer(persistConfig, settingsReducer); 45 | const persistedStore = configureStore({ 46 | reducer: { 47 | board: persistedBoard, 48 | downloads: persistedDownloads, 49 | settings: persistedSettings, 50 | }, 51 | devTools: process.env.NODE_ENV !== 'production', 52 | middleware: (getDefaultMiddleware) => 53 | getDefaultMiddleware({ 54 | serializableCheck: { 55 | ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], 56 | }, 57 | }), 58 | }); 59 | const persistor = persistStore(persistedStore); 60 | 61 | return { persistor, store: persistedStore }; 62 | }; 63 | -------------------------------------------------------------------------------- /src/renderer/App/style/dark.css: -------------------------------------------------------------------------------- 1 | body.dark-theme { 2 | --color-text-primary: #ffffff; 3 | --color-text-secondary: #2C2626; 4 | --color-background-primary: #424242; 5 | --color-background-secondary: #323232; 6 | --color-background-tertiary: #2C2626; 7 | --color-icon-primary: #4285e9; 8 | --color-icon-secondary: #D9D9D9; 9 | --color-border: #8d8686; 10 | --color-background-4: aliceblue; 11 | --color-background-5: rgba(255, 255, 255, 0.1); 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/App/style/light.css: -------------------------------------------------------------------------------- 1 | body.light-theme { 2 | --color-text-primary: rgba(0, 0, 0, 0.87); 3 | --color-text-secondary: white; 4 | --color-background-primary: rgb(231, 236, 240); 5 | --color-background-secondary: rgb(221, 231, 240);; 6 | --color-background-tertiary: rgb(220, 226, 231);; 7 | --color-icon-primary: #4285e9; 8 | --color-icon-secondary: #1c2736; 9 | --color-border: #8d8686; 10 | --color-background-4: #070708; 11 | --color-background-5: rgba(0, 0, 0, 0.1); 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/App/svg/loading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/TitleBar.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | .bold { 8 | font-weight: bold; 9 | } 10 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/TitleBar.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { useEffect, useCallback } from 'react'; 3 | import { Provider } from 'react-redux'; 4 | import { PersistGate } from 'redux-persist/integration/react'; 5 | 6 | import { TopBar } from 'renderer/TitleBar/components/TopBar'; 7 | import { store, persistor } from 'renderer/TitleBar/store/store'; 8 | 9 | import './TitleBar.scss'; 10 | import 'renderer/TitleBar/style/dark.css'; 11 | import 'renderer/TitleBar/style/light.css'; 12 | 13 | export const TitleBar = () => { 14 | const contextMenuListener = useCallback((e: MouseEvent) => { 15 | e.preventDefault(); 16 | const target = e.target as HTMLDivElement; 17 | 18 | if (target.className.includes && target.className.includes('TopBar__tab')) 19 | window.titleBar.app.showTabContextMenu({ x: e.clientX, y: e.clientY }); 20 | }, []); 21 | 22 | useEffect(() => { 23 | window.addEventListener('contextmenu', contextMenuListener); 24 | return () => window.removeEventListener('contextmenu', contextMenuListener); 25 | }, [contextMenuListener]); 26 | 27 | return ( 28 | 29 | 30 |
31 | 32 |
33 |
34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/AppControls/AppControls.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/alt-text */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 4 | /* eslint-disable import/prefer-default-export */ 5 | import { useState, useEffect } from 'react'; 6 | import CloseIcon from '@mui/icons-material/Close'; 7 | import CropSquareIcon from '@mui/icons-material/CropSquare'; 8 | import MinimizeIcon from '@mui/icons-material/Minimize'; 9 | 10 | import { Unmaximize } from './Unmaximize'; 11 | 12 | import './style.scss'; 13 | 14 | export const AppControls = () => { 15 | const [isMaximized, setIsMaxmized] = useState(false); 16 | 17 | const handleClickMaximize = () => { 18 | window.titleBar.app.maximize(); 19 | window.titleBar.app 20 | .isMaximized() 21 | .then((res) => { 22 | setIsMaxmized(res); 23 | return null; 24 | }) 25 | .catch(console.log); 26 | }; 27 | 28 | useEffect(() => { 29 | setTimeout(() => { 30 | window.titleBar.app 31 | .isMaximized() 32 | .then((res) => { 33 | setIsMaxmized(res); 34 | return null; 35 | }) 36 | .catch(console.log); 37 | }, 1500); 38 | }, []); 39 | 40 | return ( 41 |
42 |
window.titleBar.app.minimize()} 45 | > 46 | 47 |
48 |
49 | {isMaximized ? : } 50 |
51 |
window.titleBar.app.close()} 54 | > 55 | 56 |
57 |
58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/AppControls/Unmaximize/Unmaximize.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export const Unmaximize = () => { 3 | return ( 4 | 11 | 12 | 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/AppControls/Unmaximize/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Unmaximize'; 2 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/AppControls/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './AppControls'; 2 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/AppControls/style.scss: -------------------------------------------------------------------------------- 1 | #AppControls__container { 2 | display: flex; 3 | flex-direction: row; 4 | -webkit-app-region: no-drag; 5 | margin-left: 0px; 6 | 7 | .AppControls__item { 8 | padding: 5px 15px; 9 | border-radius: 3px; 10 | display: flex; 11 | align-items: center; 12 | cursor: pointer; 13 | color: var(--color-icon-primary); 14 | 15 | svg { 16 | height: 18px; 17 | } 18 | 19 | svg:hover { 20 | height: 20px; 21 | } 22 | } 23 | 24 | .AppControls__item:hover { 25 | background-color: var(--color-background-5) !important; 26 | } 27 | 28 | .close-button:hover { 29 | background-color: rgba(255, 0, 0, 0.5) !important; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/TopBar/Types.d.ts: -------------------------------------------------------------------------------- 1 | export type DownloadState = null | 'completed' | 'progressing' | 'interrupted' | 'cancelled'; 2 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/components/TopBar/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './TopBar'; 2 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | 3 | import { TitleBar } from './TitleBar'; 4 | 5 | import './TitleBar.scss'; 6 | 7 | const container = document.getElementById('root')!; 8 | const root = createRoot(container); 9 | root.render(); 10 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/store/hooks.ts: -------------------------------------------------------------------------------- 1 | import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; 2 | import type { RootState, AppDispatch } from './store'; 3 | 4 | export const useAppDispatch = () => useDispatch(); 5 | export const useAppSelector: TypedUseSelectorHook = useSelector; 6 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/store/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import { 3 | persistStore, 4 | persistReducer, 5 | FLUSH, 6 | REHYDRATE, 7 | PAUSE, 8 | PERSIST, 9 | PURGE, 10 | REGISTER, 11 | } from 'redux-persist'; 12 | import storage from 'redux-persist/lib/storage'; 13 | 14 | import tabsReducer from './reducers/Tabs'; 15 | 16 | const persistConfig = { 17 | key: 'titleBar', 18 | version: 1, 19 | storage, 20 | }; 21 | 22 | const persistedTabs = persistReducer(persistConfig, tabsReducer); 23 | 24 | export const store = configureStore({ 25 | reducer: { 26 | tabs: persistedTabs, 27 | }, 28 | devTools: process.env.NODE_ENV !== 'production', 29 | middleware: (getDefaultMiddleware) => 30 | getDefaultMiddleware({ 31 | serializableCheck: { 32 | ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], 33 | }, 34 | }), 35 | }); 36 | 37 | export const persistor = persistStore(store); 38 | 39 | export type RootState = ReturnType; 40 | export type AppDispatch = typeof store.dispatch; 41 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/style/dark.css: -------------------------------------------------------------------------------- 1 | body.dark-theme { 2 | --color-text-primary: #ffffff; 3 | --color-text-secondary: #2C2626; 4 | --color-background-primary: #424242; 5 | --color-background-secondary: #323232; 6 | --color-background-tertiary: #2C2626; 7 | --color-icon-primary: #4285e9; 8 | --color-icon-secondary: #D9D9D9; 9 | --color-border: #8d8686; 10 | --color-background-4: aliceblue; 11 | --color-background-5: rgba(255, 255, 255, 0.1); 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/TitleBar/style/light.css: -------------------------------------------------------------------------------- 1 | body.light-theme { 2 | --color-text-primary: rgba(0, 0, 0, 0.87); 3 | --color-text-secondary: white; 4 | --color-background-primary: rgb(231, 236, 240); 5 | --color-background-secondary: rgb(221, 231, 240);; 6 | --color-background-tertiary: rgb(220, 226, 231);; 7 | --color-icon-primary: #4285e9; 8 | --color-icon-secondary: #1c2736; 9 | --color-border: #8d8686; 10 | --color-background-4: #070708; 11 | --color-background-5: rgba(0, 0, 0, 0.1); 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | BonBon 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/types/analytics.ts: -------------------------------------------------------------------------------- 1 | export type EventParams = 2 | | { [key: string]: string | number | boolean } 3 | | undefined; 4 | 5 | export type Event = (eventName: string, params?: EventParams) => void; 6 | 7 | export type Page = (pageName: string, params?: EventParams) => void; 8 | 9 | export interface UserData { 10 | userId: string; 11 | appVersion: string; 12 | userIp: string; 13 | os: string; 14 | firstSeen: string; 15 | lastSeen: string; 16 | numberOfSessions: number; 17 | consent: boolean; 18 | } 19 | -------------------------------------------------------------------------------- /src/types/boards.ts: -------------------------------------------------------------------------------- 1 | import { BoardType } from "renderer/App/components/Board/Types" 2 | 3 | export type Board = BoardType 4 | 5 | export type Tag = { 6 | tag: string; 7 | inputValue?: string; 8 | }; -------------------------------------------------------------------------------- /src/types/bookmarks.ts: -------------------------------------------------------------------------------- 1 | export type Bookmark = { 2 | id: number; 3 | url: string; 4 | name: string; 5 | domain: string; 6 | host: string; 7 | tags?: string[]; 8 | }; 9 | 10 | export type Tag = { 11 | tag: string; 12 | inputValue?: string; 13 | }; 14 | 15 | export type Provider = 'Chrome' | 'Edge' | 'Brave'; 16 | -------------------------------------------------------------------------------- /src/types/configKeys.ts: -------------------------------------------------------------------------------- 1 | // types/configKeys.ts 2 | 3 | export interface ConfigKeys { 4 | 'browsing.defaultWebpage': string; 5 | 'browsing.searchEngine': string; 6 | 'browsing.width': number; 7 | 'browsing.height': number; 8 | 'browsing.size': 'lastClosed' | 'lastResized' | 'defined' | 'fit'; 9 | 'browsing.topEdge': string; 10 | 'application.backgroundGradientColors': string[]; 11 | 'application.minimapTimeout': number; 12 | 'application.minimapOn': boolean; 13 | } 14 | -------------------------------------------------------------------------------- /src/types/downloads.ts: -------------------------------------------------------------------------------- 1 | export type Download = { 2 | id: number; 3 | savePath: string; 4 | filename: string; 5 | startTime: number; 6 | date: string; 7 | }; 8 | -------------------------------------------------------------------------------- /src/types/extensions.ts: -------------------------------------------------------------------------------- 1 | import { Electron } from 'namespaces/_electronist'; 2 | 3 | export type Extension = Electron.Extension; 4 | -------------------------------------------------------------------------------- /src/types/history.ts: -------------------------------------------------------------------------------- 1 | export type History = { 2 | id: number; 3 | url: string; 4 | date: string; 5 | title: string; 6 | domain: string; 7 | host: string; 8 | }; 9 | -------------------------------------------------------------------------------- /src/types/i18n.ts: -------------------------------------------------------------------------------- 1 | export type Locale = 2 | | 'ar' 3 | | 'bn' 4 | | 'cn' 5 | | 'de' 6 | | 'en' 7 | | 'es' 8 | | 'fa' 9 | | 'fr' 10 | | 'hi' 11 | | 'ja' 12 | | 'nl' 13 | | 'pl' 14 | | 'pt' 15 | | 'ru' 16 | | 'tr'; 17 | -------------------------------------------------------------------------------- /src/types/messaging.ts: -------------------------------------------------------------------------------- 1 | // src/types/types.ts 2 | 3 | export interface ChatMessage { 4 | id: string; 5 | userId: string; 6 | username: string; 7 | message: string; 8 | timestamp: number; 9 | } 10 | 11 | export interface User { 12 | id: string; 13 | username: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/types/suggestions.ts: -------------------------------------------------------------------------------- 1 | export type DomainSuggestion = { 2 | domain: string; 3 | }; 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "commonjs", 5 | "lib": ["dom", "es2021"], 6 | "declaration": true, 7 | "declarationMap": true, 8 | "jsx": "react-jsx", 9 | "strict": true, 10 | "pretty": true, 11 | "sourceMap": true, 12 | "baseUrl": "./src", 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "noImplicitReturns": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "moduleResolution": "node", 18 | "esModuleInterop": true, 19 | "allowSyntheticDefaultImports": true, 20 | "resolveJsonModule": true, 21 | "allowJs": true, 22 | "outDir": "release/app/dist", 23 | "skipLibCheck": true, 24 | "forceConsistentCasingInFileNames": true, 25 | "incremental": true 26 | }, 27 | "include": [ 28 | ".eslintrc.js", 29 | "src/types", 30 | "src/renderer", 31 | "src/main", 32 | "src/__tests__", 33 | ".erb/scripts", 34 | ".erb/mocks", 35 | ".erb/configs", 36 | "src/namespaces/_electronist.ts" 37 | ], 38 | "exclude": ["test", "release/build", "release/app/dist", ".erb/dll", "assets"] 39 | } 40 | --------------------------------------------------------------------------------