Full Code of bepass-org/oblivion-desktop for AI

main 9fdf21769396 cached
184 files
826.2 KB
208.6k tokens
274 symbols
1 requests
Download .txt
Showing preview only (878K chars total). Download the full file or copy to clipboard to get everything.
Repository: bepass-org/oblivion-desktop
Branch: main
Commit: 9fdf21769396
Files: 184
Total size: 826.2 KB

Directory structure:
gitextract_gl2ervna/

├── .erb/
│   ├── configs/
│   │   ├── .eslintrc
│   │   ├── webpack.config.base.ts
│   │   ├── webpack.config.eslint.ts
│   │   ├── webpack.config.main.dev.ts
│   │   ├── webpack.config.main.prod.ts
│   │   ├── webpack.config.preload.dev.ts
│   │   ├── webpack.config.renderer.dev.dll.ts
│   │   ├── webpack.config.renderer.dev.ts
│   │   ├── webpack.config.renderer.prod.ts
│   │   └── webpack.paths.ts
│   ├── mocks/
│   │   └── fileMock.js
│   └── scripts/
│       ├── .eslintrc
│       ├── check-build-exists.ts
│       ├── check-native-dep.js
│       ├── check-node-env.js
│       ├── check-port-in-use.js
│       ├── clean.js
│       ├── delete-source-maps.js
│       ├── electron-rebuild.js
│       ├── link-modules.ts
│       └── notarize.js
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yaml
│   │   ├── config.yml
│   │   └── feature_request.yaml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── config.yml
│   ├── dependabot.yml
│   ├── release_message.md
│   ├── stale.yml
│   └── workflows/
│       ├── deploy-ppa.yml
│       └── publish.yml
├── .gitignore
├── .husky/
│   ├── pre-commit
│   └── pre-push
├── .prettierignore
├── .prettierrc
├── .vscode/
│   ├── launch.json
│   └── settings.json
├── @types/
│   └── node-aplay.d.ts
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DOCS.md
├── FAQ.md
├── LICENSE.md
├── README-fa.md
├── README.md
├── SECURITY.md
├── assets/
│   ├── assets.d.ts
│   ├── css/
│   │   ├── materialIcons.css
│   │   ├── noto.css
│   │   ├── shabnam.css
│   │   └── style.css
│   ├── entitlements.mac.plist
│   ├── icon.icns
│   ├── json/
│   │   └── 1713988096625.json
│   └── proto/
│       └── oblivion.proto
├── package.json
├── release/
│   └── app/
│       └── package.json
├── script/
│   ├── beforePackHook.js
│   ├── changeVersion.ts
│   ├── dlBins.ts
│   ├── makeRegeditVBSAvailable.ts
│   ├── playground.ts
│   └── postinstall.ts
├── src/
│   ├── __tests__/
│   │   └── App.test.tsx
│   ├── constants.ts
│   ├── defaultSettings.ts
│   ├── localization/
│   │   ├── am.ts
│   │   ├── ar.ts
│   │   ├── cn.ts
│   │   ├── electron.ts
│   │   ├── en.ts
│   │   ├── es.ts
│   │   ├── fa.ts
│   │   ├── id.ts
│   │   ├── index.ts
│   │   ├── my.ts
│   │   ├── pt.ts
│   │   ├── ru.ts
│   │   ├── tr.ts
│   │   ├── type.ts
│   │   ├── ur.ts
│   │   ├── useTranslate.ts
│   │   └── vi.ts
│   ├── main/
│   │   ├── config.ts
│   │   ├── dxConfig.ts
│   │   ├── ipc.ts
│   │   ├── ipcListeners/
│   │   │   ├── log.ts
│   │   │   └── settings.ts
│   │   ├── lib/
│   │   │   ├── customEvent.ts
│   │   │   ├── netStatsManager.ts
│   │   │   ├── pacScript.ts
│   │   │   ├── proxy.ts
│   │   │   ├── sbConfig.ts
│   │   │   ├── sbHelper.ts
│   │   │   ├── sbManager.ts
│   │   │   ├── speedTestManager.ts
│   │   │   ├── utils.ts
│   │   │   ├── wpHelper.ts
│   │   │   └── wpManager.ts
│   │   ├── main.ts
│   │   ├── menu.ts
│   │   ├── playground.ts
│   │   └── preload.ts
│   ├── renderer/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── BackButton.tsx
│   │   │   ├── Card/
│   │   │   │   └── index.tsx
│   │   │   ├── ConfigHandler.tsx
│   │   │   ├── Dropdown/
│   │   │   │   └── index.tsx
│   │   │   ├── Input/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useInput.ts
│   │   │   ├── Modal/
│   │   │   │   ├── DNS/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useDnsModal.ts
│   │   │   │   ├── Endpoint/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useEndpointModal.ts
│   │   │   │   ├── License/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useLicenseModal.ts
│   │   │   │   ├── MTU/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useMTUModal.ts
│   │   │   │   ├── Port/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── usePortModal.ts
│   │   │   │   ├── Profile/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useProfileModal.ts
│   │   │   │   ├── Restore/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useRestoreModal.ts
│   │   │   │   ├── RoutingRules/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useRoutingRulesModal.ts
│   │   │   │   └── TestUrl/
│   │   │   │       ├── index.tsx
│   │   │   │       └── useTestUrlModal.ts
│   │   │   ├── Nav/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useNav.ts
│   │   │   ├── Tabs.tsx
│   │   │   └── Textarea/
│   │   │       ├── index.tsx
│   │   │       └── useTextarea.ts
│   │   ├── context/
│   │   │   └── GlobalContext.tsx
│   │   ├── hooks/
│   │   │   ├── useButtonKeyDown.ts
│   │   │   └── useGoBackOnEscape.tsx
│   │   ├── index.ejs
│   │   ├── index.tsx
│   │   ├── lib/
│   │   │   ├── cfFlag.ts
│   │   │   ├── dx.ts
│   │   │   ├── getIspName.ts
│   │   │   ├── globalEvents.ts
│   │   │   ├── inputSanitizer.ts
│   │   │   ├── isAnyUndefined.ts
│   │   │   ├── loaders.ts
│   │   │   ├── settings.ts
│   │   │   ├── systemDateValidator.ts
│   │   │   ├── toPersianNumber.ts
│   │   │   ├── toasts.tsx
│   │   │   ├── utils.ts
│   │   │   └── withDefault.ts
│   │   ├── pages/
│   │   │   ├── About/
│   │   │   │   └── index.tsx
│   │   │   ├── Debug/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useDebug.ts
│   │   │   ├── Landing/
│   │   │   │   ├── DownloadProgressBar.tsx
│   │   │   │   ├── LandingBody.tsx
│   │   │   │   ├── LandingDrawer.tsx
│   │   │   │   ├── LandingHeader.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── useLanding.ts
│   │   │   ├── Network/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useOptions.ts
│   │   │   ├── Options/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useOptions.ts
│   │   │   ├── Scanner/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useScanner.ts
│   │   │   ├── Settings/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useSettings.ts
│   │   │   ├── SingBox/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useSingBox.ts
│   │   │   ├── SpeedTest/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useSpeedTest.ts
│   │   │   └── SplashScreen/
│   │   │       ├── index.tsx
│   │   │       └── useSplashScreen.ts
│   │   ├── preload.d.ts
│   │   ├── routes/
│   │   │   └── index.tsx
│   │   └── store.ts
│   └── types/
│       └── global.d.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .erb/configs/.eslintrc
================================================
{
  "rules": {
    "no-console": "off",
    "global-require": "off",
    "import/no-dynamic-require": "off"
  }
}


================================================
FILE: .erb/configs/webpack.config.base.ts
================================================
/**
 * Base webpack config used across other specific configs
 */

import webpack from 'webpack';
import TsconfigPathsPlugins from 'tsconfig-paths-webpack-plugin';
import webpackPaths from './webpack.paths';
import { dependencies as externals } from '../../release/app/package.json';

const configuration: webpack.Configuration = {
    externals: [...Object.keys(externals || {})],

    stats: 'errors-only',

    module: {
        rules: [
            {
                test: /\.[jt]sx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'ts-loader',
                    options: {
                        // Remove this line to enable type checking in webpack builds
                        transpileOnly: true,
                        compilerOptions: {
                            module: 'esnext'
                        }
                    }
                }
            }
        ]
    },

    output: {
        path: webpackPaths.srcPath,
        // https://github.com/webpack/webpack/issues/1114
        library: {
            type: 'commonjs2'
        }
    },

    /**
     * Determine the array of extensions that should be used to resolve modules.
     */
    resolve: {
        extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
        modules: [webpackPaths.srcPath, 'node_modules'],
        // There is no need to add aliases here, the paths in tsconfig get mirrored
        plugins: [new TsconfigPathsPlugins()]
    },

    plugins: [
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'production'
        })
    ]
};

export default configuration;


================================================
FILE: .erb/configs/webpack.config.eslint.ts
================================================
/* eslint import/no-unresolved: off, import/no-self-import: off */

module.exports = require('./webpack.config.renderer.dev').default;


================================================
FILE: .erb/configs/webpack.config.main.dev.ts
================================================
/**
 * Webpack config for development electron main process
 */

import path from 'path';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';

// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
    checkNodeEnv('development');
}

const configuration: webpack.Configuration = {
    devtool: 'inline-source-map',

    mode: 'development',

    target: 'electron-main',

    entry: {
        main: path.join(webpackPaths.srcMainPath, 'main.ts'),
        preload: path.join(webpackPaths.srcMainPath, 'preload.ts')
    },

    output: {
        path: webpackPaths.dllPath,
        filename: '[name].bundle.dev.js',
        library: {
            type: 'umd'
        }
    },

    plugins: [
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore
        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
            analyzerPort: 8888
        }),

        new webpack.DefinePlugin({
            'process.type': '"browser"'
        })
    ],

    /**
     * Disables webpack processing of __dirname and __filename.
     * If you run the bundle in node.js it falls back to these values of node.js.
     * https://github.com/webpack/webpack/issues/2010
     */
    node: {
        __dirname: false,
        __filename: false
    }
};

export default merge(baseConfig, configuration);


================================================
FILE: .erb/configs/webpack.config.main.prod.ts
================================================
/**
 * Webpack config for production electron main process
 */

import path from 'path';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import TerserPlugin from 'terser-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';

checkNodeEnv('production');
deleteSourceMaps();

const configuration: webpack.Configuration = {
    devtool: 'source-map',

    mode: 'production',

    target: 'electron-main',

    entry: {
        main: path.join(webpackPaths.srcMainPath, 'main.ts'),
        preload: path.join(webpackPaths.srcMainPath, 'preload.ts')
    },

    output: {
        path: webpackPaths.distMainPath,
        filename: '[name].js',
        library: {
            type: 'umd'
        }
    },

    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true
            })
        ]
    },

    plugins: [
        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
            analyzerPort: 8888
        }),

        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'production',
            DEBUG_PROD: false,
            START_MINIMIZED: false
        }),

        new webpack.DefinePlugin({
            'process.type': '"browser"'
        })
    ],

    /**
     * Disables webpack processing of __dirname and __filename.
     * If you run the bundle in node.js it falls back to these values of node.js.
     * https://github.com/webpack/webpack/issues/2010
     */
    node: {
        __dirname: false,
        __filename: false
    }
};

export default merge(baseConfig, configuration);


================================================
FILE: .erb/configs/webpack.config.preload.dev.ts
================================================
import path from 'path';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
import checkNodeEnv from '../scripts/check-node-env';

// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
    checkNodeEnv('development');
}

const configuration: webpack.Configuration = {
    devtool: 'inline-source-map',

    mode: 'development',

    target: 'electron-preload',

    entry: path.join(webpackPaths.srcMainPath, 'preload.ts'),

    output: {
        path: webpackPaths.dllPath,
        filename: 'preload.js',
        library: {
            type: 'umd'
        }
    },

    plugins: [
        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled'
        }),

        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         *
         * By default, use 'development' as NODE_ENV. This can be overriden with
         * 'staging', for example, by changing the ENV variables in the npm scripts
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'development'
        }),

        new webpack.LoaderOptionsPlugin({
            debug: true
        })
    ],

    /**
     * Disables webpack processing of __dirname and __filename.
     * If you run the bundle in node.js it falls back to these values of node.js.
     * https://github.com/webpack/webpack/issues/2010
     */
    node: {
        __dirname: false,
        __filename: false
    },

    watch: true
};

export default merge(baseConfig, configuration);


================================================
FILE: .erb/configs/webpack.config.renderer.dev.dll.ts
================================================
/**
 * Builds the DLL for development electron renderer process
 */

import webpack from 'webpack';
import path from 'path';
import { merge } from 'webpack-merge';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
import { dependencies } from '../../package.json';
import checkNodeEnv from '../scripts/check-node-env';

checkNodeEnv('development');

const dist = webpackPaths.dllPath;

const configuration: webpack.Configuration = {
    context: webpackPaths.rootPath,

    devtool: 'eval',

    mode: 'development',

    target: 'electron-renderer',

    externals: ['fsevents', 'crypto-browserify'],

    /**
     * Use `module` from `webpack.config.renderer.dev.js`
     */
    module: require('./webpack.config.renderer.dev').default.module,

    entry: {
        renderer: Object.keys(dependencies || {})
    },

    output: {
        path: dist,
        filename: '[name].dev.dll.js',
        library: {
            name: 'renderer',
            type: 'var'
        }
    },

    plugins: [
        new webpack.DllPlugin({
            path: path.join(dist, '[name].json'),
            name: '[name]'
        }),

        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'development'
        }),

        new webpack.LoaderOptionsPlugin({
            debug: true,
            options: {
                context: webpackPaths.srcPath,
                output: {
                    path: webpackPaths.dllPath
                }
            }
        })
    ]
};

export default merge(baseConfig, configuration);


================================================
FILE: .erb/configs/webpack.config.renderer.dev.ts
================================================
import 'webpack-dev-server';
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import chalk from 'chalk';
import { merge } from 'webpack-merge';
import { execSync, spawn } from 'child_process';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
import checkNodeEnv from '../scripts/check-node-env';

// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
    checkNodeEnv('development');
}

const port = process.env.PORT || 1212;
const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json');
const skipDLLs =
    module.parent?.filename.includes('webpack.config.renderer.dev.dll') ||
    module.parent?.filename.includes('webpack.config.eslint');

/**
 * Warn if the DLL is not built
 */
if (!skipDLLs && !(fs.existsSync(webpackPaths.dllPath) && fs.existsSync(manifest))) {
    console.log(
        chalk.black.bgYellow.bold(
            'The DLL files are missing. Sit back while we build them for you with "npm run build-dll"'
        )
    );
    execSync('npm run postinstall');
}

const configuration: webpack.Configuration = {
    devtool: 'inline-source-map',

    mode: 'development',

    target: ['web', 'electron-renderer'],

    entry: [
        `webpack-dev-server/client?http://localhost:${port}/dist`,
        'webpack/hot/only-dev-server',
        path.join(webpackPaths.srcRendererPath, 'index.tsx')
    ],

    output: {
        path: webpackPaths.distRendererPath,
        publicPath: '/',
        filename: 'renderer.dev.js',
        library: {
            type: 'umd'
        }
    },

    module: {
        rules: [
            {
                test: /\.s?(c|a)ss$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            sourceMap: true,
                            importLoaders: 1
                        }
                    },
                    'sass-loader'
                ],
                include: /\.module\.s?(c|a)ss$/
            },
            {
                test: /\.s?css$/,
                use: ['style-loader', 'css-loader', 'sass-loader'],
                exclude: /\.module\.s?(c|a)ss$/
            },
            // Fonts
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/i,
                type: 'asset/resource'
            },
            // Images
            {
                test: /\.(png|jpg|jpeg|gif)$/i,
                type: 'asset/resource'
            },
            // SVG
            {
                test: /\.svg$/,
                use: [
                    {
                        loader: '@svgr/webpack',
                        options: {
                            prettier: false,
                            svgo: false,
                            svgoConfig: {
                                plugins: [{ removeViewBox: false }]
                            },
                            titleProp: true,
                            ref: true
                        }
                    },
                    'file-loader'
                ]
            }
        ]
    },
    plugins: [
        ...(skipDLLs
            ? []
            : [
                  new webpack.DllReferencePlugin({
                      context: webpackPaths.dllPath,
                      manifest: require(manifest),
                      sourceType: 'var'
                  })
              ]),

        new webpack.NoEmitOnErrorsPlugin(),

        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         *
         * By default, use 'development' as NODE_ENV. This can be overriden with
         * 'staging', for example, by changing the ENV variables in the npm scripts
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'development'
        }),

        new webpack.LoaderOptionsPlugin({
            debug: true
        }),

        new ReactRefreshWebpackPlugin(),

        new HtmlWebpackPlugin({
            filename: path.join('index.html'),
            template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true
            },
            isBrowser: false,
            env: process.env.NODE_ENV,
            isDevelopment: process.env.NODE_ENV !== 'production',
            nodeModules: webpackPaths.appNodeModulesPath
        })
    ],

    node: {
        __dirname: false,
        __filename: false
    },

    devServer: {
        port,
        compress: true,
        hot: true,
        headers: { 'Access-Control-Allow-Origin': '*' },
        static: {
            publicPath: '/'
        },
        historyApiFallback: {
            verbose: true
        },
        setupMiddlewares(middlewares) {
            console.log('Starting preload.js builder...');
            const preloadProcess = spawn('npm', ['run', 'start:preload'], {
                shell: true,
                stdio: 'inherit'
            })
                .on('close', (code: number) => process.exit(code!))
                .on('error', (spawnError) => console.error(spawnError));

            console.log('Starting Main Process...');
            let args = ['run', 'start:main'];
            if (process.env.MAIN_ARGS) {
                args = args.concat(
                    ['--', ...process.env.MAIN_ARGS.matchAll(/"[^"]+"|[^\s"]+/g)].flat()
                );
            }
            spawn('npm', args, {
                shell: true,
                stdio: 'inherit'
            })
                .on('close', (code: number) => {
                    preloadProcess.kill();
                    process.exit(code!);
                })
                .on('error', (spawnError) => console.error(spawnError));
            return middlewares;
        }
    }
};

export default merge(baseConfig, configuration);


================================================
FILE: .erb/configs/webpack.config.renderer.prod.ts
================================================
/**
 * Build config for electron renderer process
 */

import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import { merge } from 'webpack-merge';
import TerserPlugin from 'terser-webpack-plugin';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';

checkNodeEnv('production');
deleteSourceMaps();

const configuration: webpack.Configuration = {
    devtool: 'source-map',

    mode: 'production',

    target: ['web', 'electron-renderer'],

    entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')],

    output: {
        path: webpackPaths.distRendererPath,
        publicPath: './',
        filename: 'renderer.js',
        library: {
            type: 'umd'
        }
    },

    module: {
        rules: [
            {
                test: /\.s?(a|c)ss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            sourceMap: true,
                            importLoaders: 1
                        }
                    },
                    'sass-loader'
                ],
                include: /\.module\.s?(c|a)ss$/
            },
            {
                test: /\.s?(a|c)ss$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
                exclude: /\.module\.s?(c|a)ss$/
            },
            // Fonts
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/i,
                type: 'asset/resource'
            },
            // Images
            {
                test: /\.(png|jpg|jpeg|gif)$/i,
                type: 'asset/resource'
            },
            // SVG
            {
                test: /\.svg$/,
                use: [
                    {
                        loader: '@svgr/webpack',
                        options: {
                            prettier: false,
                            svgo: false,
                            svgoConfig: {
                                plugins: [{ removeViewBox: false }]
                            },
                            titleProp: true,
                            ref: true
                        }
                    },
                    'file-loader'
                ]
            }
        ]
    },

    optimization: {
        minimize: true,
        minimizer: [new TerserPlugin(), new CssMinimizerPlugin()]
    },

    plugins: [
        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'production',
            DEBUG_PROD: false
        }),

        new MiniCssExtractPlugin({
            filename: 'style.css'
        }),

        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
            analyzerPort: 8889
        }),

        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true
            },
            isBrowser: false,
            isDevelopment: false
        }),

        new webpack.DefinePlugin({
            'process.type': '"renderer"'
        })
    ]
};

export default merge(baseConfig, configuration);


================================================
FILE: .erb/configs/webpack.paths.ts
================================================
const path = require('path');

const rootPath = path.join(__dirname, '../..');

const erbPath = path.join(__dirname, '..');
const erbNodeModulesPath = path.join(erbPath, 'node_modules');

const dllPath = path.join(__dirname, '../dll');

const srcPath = path.join(rootPath, 'src');
const srcMainPath = path.join(srcPath, 'main');
const srcRendererPath = path.join(srcPath, 'renderer');

const releasePath = path.join(rootPath, 'release');
const appPath = path.join(releasePath, 'app');
const appPackagePath = path.join(appPath, 'package.json');
const appNodeModulesPath = path.join(appPath, 'node_modules');
const srcNodeModulesPath = path.join(srcPath, 'node_modules');

const distPath = path.join(appPath, 'dist');
const distMainPath = path.join(distPath, 'main');
const distRendererPath = path.join(distPath, 'renderer');

const buildPath = path.join(releasePath, 'build');

export default {
    rootPath,
    erbNodeModulesPath,
    dllPath,
    srcPath,
    srcMainPath,
    srcRendererPath,
    releasePath,
    appPath,
    appPackagePath,
    appNodeModulesPath,
    srcNodeModulesPath,
    distPath,
    distMainPath,
    distRendererPath,
    buildPath
};


================================================
FILE: .erb/mocks/fileMock.js
================================================
export default 'test-file-stub';


================================================
FILE: .erb/scripts/.eslintrc
================================================
{
  "rules": {
    "no-console": "off",
    "global-require": "off",
    "import/no-dynamic-require": "off",
    "import/no-extraneous-dependencies": "off"
  }
}


================================================
FILE: .erb/scripts/check-build-exists.ts
================================================
// Check if the renderer and main bundles are built
import path from 'path';
import chalk from 'chalk';
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';

const mainPath = path.join(webpackPaths.distMainPath, 'main.js');
const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js');

if (!fs.existsSync(mainPath)) {
    throw new Error(
        chalk.whiteBright.bgRed.bold(
            'The main process is not built yet. Build it by running "npm run build:main"'
        )
    );
}

if (!fs.existsSync(rendererPath)) {
    throw new Error(
        chalk.whiteBright.bgRed.bold(
            'The renderer process is not built yet. Build it by running "npm run build:renderer"'
        )
    );
}


================================================
FILE: .erb/scripts/check-native-dep.js
================================================
import fs from 'fs';
import chalk from 'chalk';
import { execSync } from 'child_process';
import { dependencies } from '../../package.json';

if (dependencies) {
    const dependenciesKeys = Object.keys(dependencies);
    const nativeDeps = fs
        .readdirSync('node_modules')
        .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`));
    if (nativeDeps.length === 0) {
        process.exit(0);
    }
    try {
        // Find the reason for why the dependency is installed. If it is installed
        // because of a devDependency then that is okay. Warn when it is installed
        // because of a dependency
        const { dependencies: dependenciesObject } = JSON.parse(
            execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString()
        );
        const rootDependencies = Object.keys(dependenciesObject);
        const filteredRootDependencies = rootDependencies.filter((rootDependency) =>
            dependenciesKeys.includes(rootDependency)
        );
        if (filteredRootDependencies.length > 0) {
            const plural = filteredRootDependencies.length > 1;
            console.log(`
 ${chalk.whiteBright.bgYellow.bold('Webpack does not work with native dependencies.')}
${chalk.bold(filteredRootDependencies.join(', '))} ${
                plural ? 'are native dependencies' : 'is a native dependency'
            } and should be installed inside of the "./release/app" folder.
 First, uninstall the packages from "./package.json":
${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')}
 ${chalk.bold('Then, instead of installing the package to the root "./package.json":')}
${chalk.whiteBright.bgRed.bold('npm install your-package')}
 ${chalk.bold('Install the package to "./release/app/package.json"')}
${chalk.whiteBright.bgGreen.bold('cd ./release/app && npm install your-package')}
 Read more about native dependencies at:
${chalk.bold(
    'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure'
)}
 `);
            process.exit(1);
        }
    } catch (e) {
        console.log('Native dependencies could not be checked');
    }
}


================================================
FILE: .erb/scripts/check-node-env.js
================================================
import chalk from 'chalk';

export default function checkNodeEnv(expectedEnv) {
    if (!expectedEnv) {
        throw new Error('"expectedEnv" not set');
    }

    if (process.env.NODE_ENV !== expectedEnv) {
        console.log(
            chalk.whiteBright.bgRed.bold(
                `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
            )
        );
        process.exit(2);
    }
}


================================================
FILE: .erb/scripts/check-port-in-use.js
================================================
import chalk from 'chalk';
import detectPort from 'detect-port';

const port = process.env.PORT || '1212';

detectPort(port, (_err, availablePort) => {
    if (port !== String(availablePort)) {
        throw new Error(
            chalk.whiteBright.bgRed.bold(
                `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start`
            )
        );
    } else {
        process.exit(0);
    }
});


================================================
FILE: .erb/scripts/clean.js
================================================
import { rimrafSync } from 'rimraf';
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';

const foldersToRemove = [webpackPaths.distPath, webpackPaths.buildPath, webpackPaths.dllPath];

foldersToRemove.forEach((folder) => {
    if (fs.existsSync(folder)) rimrafSync(folder);
});


================================================
FILE: .erb/scripts/delete-source-maps.js
================================================
import fs from 'fs';
import path from 'path';
import { rimrafSync } from 'rimraf';
import webpackPaths from '../configs/webpack.paths';

export default function deleteSourceMaps() {
    if (fs.existsSync(webpackPaths.distMainPath))
        rimrafSync(path.join(webpackPaths.distMainPath, '*.js.map'), {
            glob: true
        });
    if (fs.existsSync(webpackPaths.distRendererPath))
        rimrafSync(path.join(webpackPaths.distRendererPath, '*.js.map'), {
            glob: true
        });
}


================================================
FILE: .erb/scripts/electron-rebuild.js
================================================
import { execSync } from 'child_process';
import fs from 'fs';
import { dependencies } from '../../release/app/package.json';
import webpackPaths from '../configs/webpack.paths';

if (Object.keys(dependencies || {}).length > 0 && fs.existsSync(webpackPaths.appNodeModulesPath)) {
    const electronRebuildCmd =
        '../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .';
    const cmd =
        process.platform === 'win32' ? electronRebuildCmd.replace(/\//g, '\\') : electronRebuildCmd;
    execSync(cmd, {
        cwd: webpackPaths.appPath,
        stdio: 'inherit'
    });
}


================================================
FILE: .erb/scripts/link-modules.ts
================================================
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';

const { srcNodeModulesPath, appNodeModulesPath, erbNodeModulesPath } = webpackPaths;

if (fs.existsSync(appNodeModulesPath)) {
    if (!fs.existsSync(srcNodeModulesPath)) {
        fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction');
    }
    if (!fs.existsSync(erbNodeModulesPath)) {
        fs.symlinkSync(appNodeModulesPath, erbNodeModulesPath, 'junction');
    }
}


================================================
FILE: .erb/scripts/notarize.js
================================================
const { notarize } = require('@electron/notarize');
const { build } = require('../../package.json');

exports.default = async function notarizeMacos(context) {
    const { electronPlatformName, appOutDir } = context;
    if (electronPlatformName !== 'darwin') {
        return;
    }

    if (process.env.CI !== 'true') {
        console.warn('Skipping notarizing step. Packaging is not running in CI');
        return;
    }

    if (!('APPLE_ID' in process.env && 'APPLE_APP_SPECIFIC_PASSWORD' in process.env)) {
        console.warn(
            'Skipping notarizing step. APPLE_ID and APPLE_APP_SPECIFIC_PASSWORD env variables must be set'
        );
        return;
    }

    const appName = context.packager.appInfo.productFilename;

    await notarize({
        appBundleId: build.appId,
        appPath: `${appOutDir}/${appName}.app`,
        appleId: process.env.APPLE_ID,
        appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD
    });
};


================================================
FILE: .eslintignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Coverage directory used by tools like istanbul
coverage
.eslintcache

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# OSX
.DS_Store

release/app/dist
release/build
.erb/dll

.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts

# eslint ignores hidden directories by default:
# https://github.com/eslint/eslint/issues/8429
!.erb


================================================
FILE: .eslintrc.js
================================================
module.exports = {
    extends: 'erb',
    plugins: ['@typescript-eslint'],
    rules: {
        // A temporary hack related to IDE not resolving correct package.json
        'import/no-extraneous-dependencies': 'off',
        'react/react-in-jsx-scope': 'off',
        'react/jsx-filename-extension': 'off',
        'import/extensions': 'off',
        'import/no-unresolved': 'off',
        'import/no-import-module-exports': 'off',
        'no-shadow': 'off',
        '@typescript-eslint/no-shadow': 'error',
        'no-unused-vars': 'off',
        '@typescript-eslint/no-unused-vars': 'warn',
        'no-nested-ternary': 'off',
        'jsx-a11y/label-has-associated-control': 'warn',
        'no-console': 'off',
        'import/prefer-default-export': 'off',
        'prefer-destructuring': 'off',
        'prettier/prettier': 'off',
        'react/jsx-no-useless-fragment': 'off',
        'react/jsx-curly-brace-presence': 'off',
        'jsx-a11y/click-events-have-key-events': 'warn',
        'jsx-a11y/no-static-element-interactions': 'warn',
        'react/button-has-type': 'off',
        'prefer-template': 'off',
        'no-param-reassign': 'off',
        'no-lonely-if': 'off',
        'no-plusplus': 'off',
        'promise/always-return': 'off',
        'class-methods-use-this': 'off',
        'react-hooks/exhaustive-deps': 'warn',
        'object-shorthand': 'off',
        'import/no-cycle': 'off',
        'promise/catch-or-return': 'off',
        'import/order': 'warn',
        'spaced-comment': 'off',
        'react/jsx-boolean-value': 'off',
        'react/require-default-props': 'off',
        'no-unneeded-ternary': 'off',
        'consistent-return': 'off',
        'no-async-promise-executor': 'off',
        'no-else-return': 'off',
        'func-names': 'off',
        'prefer-promise-reject-errors': 'off',
        'react/function-component-definition': 'off'
    },
    parserOptions: {
        ecmaVersion: 2022,
        sourceType: 'module'
    },
    settings: {
        'import/resolver': {
            // See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
            node: {},
            webpack: {
                config: require.resolve('./.erb/configs/webpack.config.eslint.ts')
            },
            typescript: {}
        },
        'import/parsers': {
            '@typescript-eslint/parser': ['.ts', '.tsx']
        }
    }
};


================================================
FILE: .gitattributes
================================================
*       text    eol=lf
*.exe   binary
*.png   binary
*.jpg   binary
*.jpeg  binary
*.ico   binary
*.icns  binary
*.eot   binary
*.otf   binary
*.ttf   binary
*.woff  binary
*.woff2 binary


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yaml
================================================
name: Bug Report
description: Report a bug encountered while using Oblivion Desktop
body:
    - type: markdown
      attributes:
          value: |
              Please make sure to provide a descriptive report. It saves time for both the developers and users who are looking for solutions. Providing as much information as possible, including screenshots and logs, is highly appreciated. This will help us to better understand the issue and respond more effectively.
              لطفا مطمئن شوید که گزارشی تشریحی ارائه دهید. این باعث می‌شود که توسعه‌دهندگان و کاربرانی که در جستجوی راهکارها هستند، زمان کمتری صرف کنند. ارائه اطلاعات بیشتر از جمله اسکرین‌شات و لاگ توصیه می‌شود. این باعث می‌شود که ما بهتر بتوانیم مشکل را درک کرده و به‌صورت بهتر پاسخ دهیم.
    - type: checkboxes
      id: confirm-search
      attributes:
          label: Attention | توجه
          description: Please search [existing issues](https://github.com/bepass-org/oblivion-desktop/issues) before reporting | لطفا قبل از ارسال گزارش [ایشوهای موجود](https://github.com/bepass-org/oblivion-desktop/issues) را بررسی کنید.
          options:
              - label: I searched and no similar issues were found | جستجو کردم و هیچ گزارش مشابهی پیدا نشد
                required: true
    - type: textarea
      id: problem
      attributes:
          label: What Happened? | چه اتفاقی افتاده؟
          description: |
              Please provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner.
              لطفاً اطلاعات کاملی را ارائه دهید. اگر این کار را انجام ندهید، ممکن است مشکل شما به صورت تمام شده تلقی شده یا در زمان مناسبی بررسی نشود.
      validations:
          required: true
    - type: textarea
      id: reproduce
      attributes:
          label: Minimal Reproducible Example | چه پروسه‌ای برای مشاهده این مشکل طی کرده‌اید؟
          placeholder: |
              1. Go to '...'
              2. Click on '....'
              3. Scroll down to '....'
              4. See error
    - type: textarea
      id: logs
      attributes:
          label: Relevant log output | لاگ برنامه
          description: Refer to the program menu and copy the log and send it to us.
          render: shell
      validations:
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
    - name: Wiki
      url: https://github.com/bepass-org/oblivion-desktop/wiki
      about: A set of guidelines for using the application and troubleshooting issues.
    - name: FAQ
      url: https://github.com/bepass-org/oblivion-desktop/blob/main/FAQ.md
      about: Before asking a question or presenting a problem, read through the frequently asked questions.
    - name: In Progress
      url: https://github.com/orgs/bepass-org/projects/4/views/1
      about: a List of work in progress & items planned for development ...


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yaml
================================================
name: Feature Request
description: Request a new feature
labels: ['enhancement']

body:
    - type: markdown
      attributes:
          value: |
              Here is a template you can use to share your idea.
              اینجا می‌توانید ایده خود به اشتراک بگذارید.
    - type: textarea
      attributes:
          label: Feature description | توضیحات ویژگی
          description: Please provide a clear and concise description of what you want to happen and what problem will this solve | لطفاً توضیحات روشن و کوتاهی از آنچه که می‌خواهید رخ دهد و مشکلی که پیش می‌آید را حل کنید، ارائه دهید.
      validations:
          required: true


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Change Description

What does this change add to or fix in the project?

### Checklist

- [ ] Code has been tested.
    - [ ] Windows 10
    - [ ] Windows 11
    - [ ] macOS
    - [ ] Linux
- [ ] Relevant documentation has been updated.

### Related Links

Link to any related issues. Example: Closes #123


================================================
FILE: .github/config.yml
================================================
requiredHeaders:
    - Prerequisites
    - Expected Behavior
    - Current Behavior
    - Possible Solution
    - Your Environment


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
    - package-ecosystem: 'npm'
      directory: '/'
      schedule:
          interval: 'monthly'
      ignore:
          - dependency-name: '*'
            update-types:
                - 'version-update:semver-major'


================================================
FILE: .github/release_message.md
================================================
## Pre-release RELEASE_TAG has been released for Windows, Linux & macOS.

### Some software changes:

- [x] Fixed some minor bugs

#### To report issues or provide suggestions:

https://github.com/bepass-org/oblivion-desktop/issues

<div dir="rtl">

## نسخه پیش‌ازانتشار RELEASE_TAG برای ویندوز، لینوکس و مک منتشر شد.

### برخی‌از تغییرات برنامه:

- [x] رفع برخی‌از اشکالات جزئی

#### گزارش مشکل یا ارائه پیشنهاد:

https://github.com/bepass-org/oblivion-desktop/issues

</div>

<hr />

## Download

<!-- [![Release Downloads](https://img.shields.io/github/downloads/oblivion-desktop/RELEASE_TAG/total?style=flat-square&logo=github)](https://img.shields.io/github/downloads/bepass-org/oblivion-desktop/RELEASE_TAG/) -->

<div align="left" id="download">
    <table>
        <thead align="left">
            <tr>
                <th>OS / Arch</th>
                <th>Compatibility</th>
            </tr>
        </thead>
        <tbody align="left">
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-win-x64.exe"><img src="https://img.shields.io/badge/Windows-Setup x64-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-win-x64.zip"><img src="https://img.shields.io/badge/Windows-Portable x64-005AA8.svg?logo=gitforwindows"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-win-arm64.exe"><img src="https://img.shields.io/badge/Windows-Setup arm64-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-win-arm64.zip"><img src="https://img.shields.io/badge/Windows-Portable arm64-005AA8.svg?logo=gitforwindows"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-win-ia32.exe"><img src="https://img.shields.io/badge/Windows-Setup x86-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-win-ia32.zip"><img src="https://img.shields.io/badge/Windows-Portable x86-005AA8.svg?logo=gitforwindows"></a>
                </td>
                <td>
                    10+<br>
                </td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-mac-arm64.dmg"><img src="https://img.shields.io/badge/macOS-DMG arm64-F0F0F1.svg?logo=apple"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-mac-arm64.zip"><img src="https://img.shields.io/badge/macOS-ZIP arm64-9e9e9e.svg?logo=apple" /></a><br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-mac-x64.dmg"><img src="https://img.shields.io/badge/macOS-DMG x64-F0F0F1.svg?logo=apple"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-mac-x64.zip"><img src="https://img.shields.io/badge/macOS-ZIP x64-9e9e9e.svg?logo=apple" /></a>
                </td>
                <td>10.15+</td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-amd64.deb"><img src="https://img.shields.io/badge/Linux-DEB x64-DC470E.svg?logo=debian"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-x86_64.rpm"><img src="https://img.shields.io/badge/Linux-RPM x64-01ABD2.svg?logo=redhat"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-x64.tar.xz"><img src="https://img.shields.io/badge/Linux-tar.xz x64-EDC204.svg?logo=linux"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-x86_64.AppImage"><img src="https://img.shields.io/badge/Linux-AppImage x64-bf7645.svg?logo=linux"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-arm64.deb"><img src="https://img.shields.io/badge/Linux-DEB arm64-DC470E.svg?logo=debian"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-aarch64.rpm"><img src="https://img.shields.io/badge/Linux-RPM arm64-01ABD2.svg?logo=redhat"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-arm64.tar.xz"><img src="https://img.shields.io/badge/Linux-tar.xz arm64-EDC204.svg?logo=linux"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/download/RELEASE_TAG/oblivion-desktop-linux-arm64.AppImage"><img src="https://img.shields.io/badge/Linux-AppImage arm64-bf7645.svg?logo=linux"></a>
                </td>
                <td>
                    Gnome<br>
                    KDE
                </td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion/releases/latest"><img src="https://img.shields.io/badge/Android-APK Universal-0d7365.svg?logo=android"></a>
                    <a href="https://play.google.com/store/apps/details?id=org.bepass.oblivion"><img src="https://img.shields.io/badge/Android-APK Universal-044d29.svg?logo=googleplay"></a>
                </td>
                <td>6+</td>
            </tr>
            <tr>
                <td>
                    <img src="https://img.shields.io/badge/iOS-Coming soon ...-pink.svg?logo=apple">
                </td>
                <td></td>
            </tr>
        </tbody>
    </table>
</div>


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
    - discussion
    - security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
    This issue has been automatically marked as stale because it has not had
    recent activity. It will be closed if no further activity occurs. Thank you
    for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false


================================================
FILE: .github/workflows/deploy-ppa.yml
================================================
name: Build and Upload to Launchpad PPA

on:
    workflow_dispatch: {}

jobs:
    build-and-upload:
        runs-on: ubuntu-latest

        env:
            GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
            LAUNCHPAD_USERNAME: ${{ secrets.LAUNCHPAD_USERNAME }}
            LAUNCHPAD_PASSWORD: ${{ secrets.LAUNCHPAD_PASSWORD }}

        steps:
            - name: Checkout repository
              uses: actions/checkout@v3

            - name: Install dependencies
              run: |
                  sudo apt-get update
                  sudo apt-get install -y jq devscripts debhelper dh-make dput gnupg lintian build-essential

            - name: Extract package name and version from package.json
              id: extract
              run: |
                  echo "PACKAGE_NAME=$(jq -r '.name' package.json)" >> $GITHUB_ENV
                  echo "VERSION=$(jq -r '.version' package.json)" >> $GITHUB_ENV

            - name: Prepare Debian packaging directory
              run: |
                  rm -rf ${PACKAGE_NAME}-${VERSION}
                  mkdir ${PACKAGE_NAME}-${VERSION}
                  cp -r . ${PACKAGE_NAME}-${VERSION}/
                  cd ${PACKAGE_NAME}-${VERSION}
                  dh_make -s -y -p ${PACKAGE_NAME}_${VERSION}
                  rm -f debian/*.ex debian/*.EX debian/README.*
                  printf "%s\n" "${PACKAGE_NAME} (${VERSION}) unstable; urgency=medium" "" "  * Initial release." "" " -- Oblivion Desktop <$LAUNCHPAD_USERNAME>  $(date -R)" > debian/changelog

            - name: Setup GPG for signing commits and packages
              run: |
                  echo "${{ secrets.GPG_PRIVATE_KEY }}" > private.key
                  gpg --batch --import private.key
                  git config --global user.signingkey "$GPG_KEY_ID"
                  git config --global commit.gpgsign true
                  git config --global user.name "Oblivion Desktop"
                  git config --global user.email "$LAUNCHPAD_USERNAME"

            - name: Build source package
              run: |
                  cd ${PACKAGE_NAME}-${VERSION}
                  debuild -S -sa -k"$GPG_KEY_ID"

            - name: Sign changes and dsc files
              run: |
                  cd ..
                  gpg --batch --yes -u "$GPG_KEY_ID" --armor --detach-sign ${PACKAGE_NAME}_${VERSION}_source.changes
                  gpg --batch --yes -u "$GPG_KEY_ID" --armor --detach-sign ${PACKAGE_NAME}_${VERSION}.dsc

            - name: Upload source package to Launchpad PPA
              run: |
                  dput ppa ${PACKAGE_NAME}_${VERSION}_source.changes


================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish

permissions: write-all

on:
    push:
        tags:
            - '*'

jobs:
    create-release:
        runs-on: ubuntu-22.04
        steps:
            - name: Checkout git repo
              uses: actions/checkout@v4

            - name: Prepare Release Message
              run: |
                  sed 's|RELEASE_TAG|${{ github.ref_name }}|g' ./.github/release_message.md >> release.md

            - name: Set Release Message
              uses: softprops/action-gh-release@v2
              env:
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              with:
                  tag_name: ${{ github.ref_name }}
                  body_path: './release.md'
                  draft: true

    publish:
        needs: create-release
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-22.04, ubuntu-24.04, windows-latest, macos-13, macos-14]

        steps:
            - name: Checkout git repo
              uses: actions/checkout@v4

            - name: Install Node and NPM
              uses: actions/setup-node@v4
              with:
                  node-version: 20
                  cache: npm

            - name: Install and build
              run: |
                  npm install
                  if [ "$RUNNER_OS" == "macOS" ]; then
                    npm install dmg-license
                  fi
                  npm run build
              shell: bash

            - name: Publish releases mac x64
              if: matrix.os == 'macos-13'
              env:
                  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              run: npm exec electron-builder -- --mac dmg zip --x64 --publish onTagOrDraft

            - name: Publish releases mac arm64
              if: matrix.os == 'macos-14'
              env:
                  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              run: npm exec electron-builder -- --mac dmg zip --arm64 --publish onTagOrDraft

            - name: Publish releases windows
              if: matrix.os == 'windows-latest'
              env:
                  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              run: npm exec electron-builder -- --publish onTagOrDraft

            - name: Publish releases GNU Linux x64
              if: matrix.os == 'ubuntu-22.04'
              env:
                  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              run: npm exec electron-builder -- --linux deb rpm tar.xz AppImage --x64 --publish onTagOrDraft

            - name: Publish releases GNU Linux arm64
              if: matrix.os == 'ubuntu-24.04'
              env:
                  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              run: npm exec electron-builder -- --linux deb rpm tar.xz AppImage --arm64 --publish onTagOrDraft

            - name: Delete unwanted files
              uses: mknejp/delete-release-assets@v1
              with:
                  token: ${{ secrets.GITHUB_TOKEN }}
                  tag: ${{ github.ref_name }}
                  fail-if-no-assets: false
                  assets: |
                      oblivion-desktop-win.exe
                      oblivion-desktop-mac-arm64.zip.blockmap
                      oblivion-desktop-mac-x64.zip.blockmap
                      latest-linux-arm64.yml
                      latest-linux.yml
                      latest-mac.yml
                      latest.yml
                      oblivion-desktop-mac-arm64.dmg.blockmap
                      oblivion-desktop-mac-x64.dmg.blockmap


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Coverage directory used by tools like istanbul
coverage
.eslintcache

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# OSX
.DS_Store

release/app/dist
release/build
.erb/dll

.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts
*.tsbuildinfo

log.txt
warp-plus*.zip
masque-plus*.zip
assets/bin
stuff
ca.psiphon.PsiphonTunnel.tunnel-core
oblivion-helper*.zip
zag-netStats*.zip
proxy-reset*.zip

================================================
FILE: .husky/pre-commit
================================================


================================================
FILE: .husky/pre-push
================================================


================================================
FILE: .prettierignore
================================================
.erb
.vscode
node_modules
.git

# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Coverage directory used by tools like istanbul
coverage
.eslintcache

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# OSX
.DS_Store

release/app/dist
release/build
.github/release_message.md
.erb/dll

.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts

# eslint ignores hidden directories by default:
# https://github.com/eslint/eslint/issues/8429
!.erb

bin
README.md
stuff

assets/*
!assets/css

================================================
FILE: .prettierrc
================================================
{
    "tabWidth": 4,
    "useTabs": false,
    "singleQuote": true,
    "jsxSingleQuote": true,
    "printWidth": 100,
    "trailingComma": "none"
}


================================================
FILE: .vscode/launch.json
================================================
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Electron: Main",
      "type": "node",
      "request": "launch",
      "protocol": "inspector",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "start"],
      "env": {
        "MAIN_ARGS": "--inspect=5858 --remote-debugging-port=9223"
      }
    },
    {
      "name": "Electron: Renderer",
      "type": "chrome",
      "request": "attach",
      "port": 9223,
      "webRoot": "${workspaceFolder}",
      "timeout": 15000
    }
  ],
  "compounds": [
    {
      "name": "Electron: All",
      "configurations": ["Electron: Main", "Electron: Renderer"]
    }
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "files.associations": {
    ".eslintrc": "jsonc",
    ".prettierrc": "jsonc",
    ".eslintignore": "ignore"
  },
  
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "html",
    "typescriptreact"
  ],

  "javascript.validate.enable": false,
  "javascript.format.enable": false,
  "typescript.format.enable": false,

  "search.exclude": {
    ".git": true,
    ".eslintcache": true,
    ".erb/dll": true,
    "release/{build,app/dist}": true,
    "node_modules": true,
    "npm-debug.log.*": true,
    "test/**/__snapshots__": true,
    "package-lock.json": true,
    "*.{css,sass,scss}.d.ts": true
  },
  "cSpell.words": [
    "asar",
    "Authmode",
    "bepass",
    "cfon",
    "electronmon",
    "fullscreenable",
    "Ghobadi",
    "gool",
    "gsettings",
    "hiddify",
    "HKCU",
    "ircf",
    "ircfspace",
    "kiomarzsss",
    "kioslaverc",
    "kwriteconfig",
    "networksetup",
    "nsis",
    "Pashmfouroush",
    "plusplus",
    "psiphon",
    "regedit",
    "shabnam",
    "webm",
    "webp",
    "wintun",
    "worktree",
    "Yousef",
    "zustand",
    "اتصال",
    "اجرا",
    "اعمال",
    "اندپوینت",
    "برنامه",
    "پروکسی",
    "پشتیبانی",
    "پورت",
    "پیکربندی",
    "تلاش",
    "توسط",
    "حاضر",
    "حالت",
    "دارد",
    "دسکتاپ",
    "دیگری",
    "روبرو",
    "سنجی",
    "صورت",
    "فایل",
    "کلودفلر",
    "لازم",
    "لایسنس",
    "متصل",
    "متوجه",
    "مجدد",
    "مجدداً",
    "محیط",
    "مواجه",
    "نیاز",
    "نیازمند",
    "نیست",
    "هویت",
    "وارد",
    "یافت"
  ],
  "editor.formatOnSave": false,
  "yaml.validate": false
}


================================================
FILE: @types/node-aplay.d.ts
================================================
declare module 'node-aplay' {
    export default class Aplay {
        constructor(file: string);

        play(): void;

        pause(): void;

        resume(): void;

        stop(): void;

        on(event: 'complete' | 'error', listener: (...args: any[]) => void): this;
    }
}


================================================
FILE: CHANGELOG.md
================================================
<https://github.com/bepass-org/oblivion-desktop/releases>


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

- The use of sexualized language or imagery, and sexual attention or
  advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
  address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
ircfspace@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: CONTRIBUTING.md
================================================
# CONTRIBUTION GUIDE

Thank you for your interest in contributing to Oblivion Desktop! We appreciate your help and support. - We have tried our best to provide some documentation on how to contribute to this project; here are some guidelines:

## Where you can help?

- ### 💻 Contributing to the development

    if you have programming skills that can be helpful:
    Pick up issues/tasks from [issues page](https://github.com/bepass-org/oblivion-desktop/issues) or the [project board](https://github.com/orgs/bepass-org/projects/4) or suggest your own features, fixes, etc.  
     Before starting a new task, please coordinate with the collaborators to ensure no one else is working on it.
    - #### Setup your development environment
        - Fork this [repository](https://github.com/bepass-org/oblivion-desktop)
        - Follow the instructions in the [DOCS.md](DOCS.md) to set up your development environment and bring up your development server
        - Make your changes and create a pull request (PR)

    - #### Code Style Guide
        To maintain code quality, ensure your PR follows our TypeScript style guidelines and passes type checking (`tsc`) - we use Prettier (2 spaces, single quotes) for consistent formatting and Husky hooks (automatically installed via `npm install` or manually with `npm run prepare`) to run `prettier` on commit and type checking on push, while you can manually fix style issues with `npm run format`.

- ### 🌐 Translating to other languages

    The current translation is done from English to other languages. Persian and English are translated manually, while other languages are translated with the help of AI. Your contributions to improving the translation and providing feedback are welcome.
    - We prioritize translations for languages spoken in regions with systemic internet censorship that broadly restricts access to information, similar to the digital environments in Iran, China, and Russia. Our program focuses on supporting communities where:
      (a) Internet restrictions disproportionately impacts general freedoms of expression and access to information
      (b) Censorship extends beyond targeted law enforcement against illegal activities.
      For countries where online restrictions are primarily applied to combat criminal content (e.g., child exploitation materials, terrorist propaganda, or hate speech), English remains the supported interface language. This allows us to concentrate our limited resources where they can have the greatest impact in promoting open access to information. We evaluate all translation requests against these criteria to ensure alignment with our mission of fighting unjust censorship.
    - If you want to edit the translated text in this application, you can directly edit the files in the [src/localization](src/localization) directory.
    - If you're interested in translating markdown (.md) files for new languages, simply create the language file next to the original file. (We will take care of the directory structure if needed.)

- ### 💬 Discussions

    If you have any questions, suggestions, complaints, or concerns that are not addressed in our document(s), please feel free to open an [issue](https://github.com/bepass-org/oblivion-desktop/issues) or directory contact us as we are willing to discuss about this project at any time.

### ⚠️ Warning!

We recommend not to use your real legal name while contributing to this project; it may find you trouble in many countries that have problems with free circulation of information.


================================================
FILE: DOCS.md
================================================
# DEVELOPER DOCUMENTATION

Oblivion Desktop is an [Electron](https://www.electronjs.org/) project bootstrapped with [Electron React Boilerplate.
](https://github.com/electron-react-boilerplate/electron-react-boilerplate)

In a nutshell, Oblivion Desktop is a GUI program that interacts with "[WARP-Plus](https://github.com/bepass-org/warp-plus/)"'s binary executable and changes the system's proxy settings.

<!-- and [sing-box](https://sing-box.sagernet.org/alig) binaries. -->

## Getting Started

1. Make sure you have [Node.js](https://nodejs.org/) and [NPM](https://www.npmjs.com/) installed on your system.

2. Clone this repository (`$ git clone https://github.com/bepass-org/oblivion-desktop.git`)

3. Install the program's dependencies:

```shell
npm install
```

4. Run the development server:

```shell
npm run dev # or npm start
```

## Packaging for Production (build from the source)

To package for your local platform:

```shell
npm run package
```

for faster production build (test purposes) use one of the following:

```shell
npm run package:linux
npm run package:windows
npm run package:mac
```

When the command(s) finish, you are to have your production build(s) at `release/build`!

for more specific builds checkout: https://www.electron.build/cli
For more specific builds, take a look at [this](https://electron.build/cli)!

## IPC (sending data between main and renderer)

as you may be familiar with electron already.  
As you are probably familiar with [Electron](https://electron.build/) already;
We need to use [IPC](https://www.electronjs.org/docs/latest/tutorial/ipc) in order to send and receive data between main and renderer.  
Take a loot at `src/main/ipc.ts` and `src/renderer/index.tsx` for an in-action example.

# Codebase Terminology

For clarity when working with the TypeScript codebase:

### Dependencies

After Warp-Plus (wp) updates, always refresh dependencies:

```bash
npm install
Code Abbreviations
```

<b>wp:</b> WARP-Plus module (Cloudflare integration)

<b>od:</b> Oblivion Desktop core functionality

<b>hp:</b> OblivionHelper utility package

<b>TypeScript Conventions</b>

- All abbreviations should be typed explicitly:

```ts
interface WpConfig { /* Warp-plus settings */ }
type OdState = /* OblivionDesktop state */;
Avoid inline abbreviations - use proper type aliases
```

- Document abbreviations in JSDoc:

```ts
/** @param wpConfig - Warp-plus configuration object */
Maintenance;
```

The project uses:

Strict TypeScript (strict: true)

Consistent ESLint rules

Pre-commit type checking## Notes

- (After WP updates;) to get the latest WP version, that app is using. run: `npm i`.
- `wp` refers to `warp-plus` in the source code.
- `od` refers to `oblivion desktop` in the source code.
- `hp` refers to `oblivion helper` in the source code.

Note: On Linux/MacOS, configuration files for the program are in `~/.config/oblivion-desktop` (that's `/home/user/.config/oblivion-desktop` as realpath) - **do not touch these files.**

Happy hacking! 😉


================================================
FILE: FAQ.md
================================================
# سوالات متداول

- [کاربرد اپ Oblivion Desktop چیه؟](#کاربرد-اپ-oblivion-desktop-چیه)
- [این برنامه چطور کار می‌کنه؟](#این-برنامه-چطور-کار-میکنه)
- [چرا گاهی اوقات IP ایران نمایش داده می‌شه؟](#چرا-گاهی-اوقات-ip-ایران-نمایش-داده-میشه)
- [برای تغییر IP وارپ چکار کنم؟](#برای-تغییر-ip-وارپ-چکار-کنم)
- [تفاوت اپ Warp با Warp-Plus و Oblivion چیست؟](#تفاوت-اپ-warp-با-warp-plus-و-oblivion-چیست)
- [سازنده اصلی WarpInWarp کجاست؟](#سازنده-اصلی-warpinwarp-کجاست)
- [آیا Oblivion امنه؟](#آیا-oblivion-امنه)
- [آیا سرویس وارپ کلودفلر امنه؟](#آیا-سرویس-وارپ-کلودفلر-امنه)
- [هدف از این‌سرویس رایگان چیه؟](#هدف-از-این‌سرویس-رایگان-چیه)

# Frequently Asked Questions

- [What is the purpose of the Oblivion Desktop app?](#what-is-the-purpose-of-the-oblivion-desktop-app)
- [How does this application work?](#how-does-this-application-work)
- [Why does my IP sometimes show as Iranian?](#why-does-my-ip-sometimes-show-as-iranian)
- [How can I change my Warp IP?](#how-can-i-change-my-warp-ip)
- [Where is the original creator of WarpInWarp?](#where-is-the-original-creator-of-warpinwarp)
- [Is Oblivion secure?](#is-oblivion-secure)
- [Is the Warp Cloudflare service secure?](#is-the-warp-cloudflare-service-secure)
- [What is the aim of this free service?](#what-is-the-aim-of-this-free-service)

## کاربرد اپ Oblivion Desktop چیه؟

این‌برنامه یک نسخه غیررسمی، اما قابل اطمینان از اپ Oblivion یا فراموشی است که برای ویندوز، لینوکس و مک ارائه گردیده
است.<br />برنامه Oblivion Desktop با الگو گرفتن از رابط کاربری نسخه اصلی که توسط یوسف قبادی برنامه‌نویسی شده بود، با هدف
دسترسی آزاد به اینترنت تهیه گردیده است.

## این برنامه چطور کار می‌کنه؟

اپ Oblivion Desktop عملاً یک VPN امن و مطمئن برای عبور از فیلترینگ و دورزدن تحریم‌ها هست، که از هسته Warp-Plus استفاده
می‌کنه.

## چرا گاهی اوقات IP ایران نمایش داده می‌شه؟

آی‌پی مذکور، IP اصلی کاربر نبوده و سرویس Warp کلودفلر متناسب با لوکیشن کاربر یکی‌از آی‌پی‌های خودش رو برگشت میده و
لوکیشن ایران رو براش جا می‌زنه، که عملن در این‌حالت نمی‌شه از سد تحریم‌ها گذشت.

## برای تغییر IP وارپ چکار کنم؟

متدهای گول و سایفون در تنظیمات برنامه این‌امکان رو به‌کاربر میدن که بتونه IP خودش رو به لوکیشنی غیراز ایران تغییر بده و
امتحان‌کردن این‌گزینه‌ها توی شرایط پراختلال اینترنت توصیه می‌شه.

## تفاوت اپ Warp با Warp-Plus و Oblivion چیست؟

اپ Oblivion یا هسته Warp-Plus با استفاده از سرویس وارپ کلودفلر کار کرده، اما برتری Warp-Plus و نرم‌افزار Oblivion استفاده از متدهای گول و سایفون است، که امکان تغییر آی‌پی را برای کاربر فراهم می‌کنند.

## سازنده اصلی WarpInWarp کجاست؟

از وضعیت #یوسف_قبادی که چندماه قبل بازداشت شده‌بود اطلاعی در دسترس نیست. البته درحال‌حاضر مسیر توسعه Oblivion و
Warp-Plus توسط سایر توسعه‌دهندگان به پیش میره.

## آیا Oblivion امنه؟

اپ‌های Oblivion و Oblivion Desktop و همینطور هسته Warp-Plus متن‌باز هستن و سورسشون در گیت‌هاب قرار گرفته، ضمن اینکه بیلد
پروژه‌ها هم به‌صورت خودکار توسط گیت‌هاب اکشن انجام می‌شن.

## آیا سرویس وارپ کلودفلر امنه؟

وارپ به‌وسیله رمزنگاری مقدار بیشتری از ترافیک خروجی از دستگاهتون، اجازه نمیده هیچ‌کس در کار شما سرک بکشه.

## هدف از این‌سرویس رایگان چیه؟

تحقق شعار اینترنت برای همه یا هیچ‌کس؛ و همینطور دسترسی آزاد مردم، به‌خصوص قشر کم‌برخوردار به اینترنت.

## What is the purpose of the Oblivion Desktop app?

This application is an unofficial yet reliable version of the Oblivion or forgetfulness app provided for Windows, Linux,
and MacOS. The Oblivion Desktop program is designed with the purpose of providing unrestricted access to the internet by adopting
the user interface pattern of the original version developed by Youssef Ghobadi.

## How does this application work?

The Oblivion Desktop app essentially functions as a secure VPN for bypassing filtering and circumventing sanctions,
utilizing the WARP-Plus core.

## Why does my IP sometimes show as Iranian?

The displayed IP is not the user's actual IP; rather, the Cloudflare WARP service sometimes assigns one of its own IPs
to correspond with the user's location and may display Iran as the location. In such cases, it's practically impossible
to bypass sanctions.

## How can I change my WARP IP?

Methods such as Gool and Psiphon in the application settings provide users with the capability to change their IP to a
location other than Iran. It is recommended to explore these options, especially in situations of internet disruption.

## Where is the original creator of WarpInWarp?

There is currently no information available regarding the status of Youssef Ghobadi, who was detained several months
ago. However, development of Oblivion and Warp-Plus is currently progressing under the guidance of other developers.

## Is Oblivion secure?

Both the Oblivion and Oblivion Desktop programs, as well as the WARP-Plus core, are open-source and their source code is
available on GitHub. Additionally, project builds are automatically performed by GitHub Actions.

## Is the WARP Cloudflare service secure?

WARP encrypts a larger amount of outgoing traffic from your device, preventing anyone from interfering with your work.

## What is the purpose of this program?

To realize the slogan of "Internet for all or for none," as well as to provide free access to the internet, especially
for underserved populations.


================================================
FILE: LICENSE.md
================================================
# Restrictive License - No Sale and Modification Limitation

Copyright © 2025 Oblivion Desktop

This software is provided under a specific license.

<b>by using this software or accessing its source code, you agree that:</b>

- No Sale: You are not allowed to sell or distribute modified versions of it.
- Modification Limitation: You may not alter the name, about page, copyright notice, logo, or other fundamental details of this software. However, distribution of the software in binary form is allowed, provided the original license and copyright notice remain intact.

Development of the software is only possible through project participation.

---

# لایسنس محدود - عدم امکان فروش و محدودیت تغییرات

کپی‌رایت © ۱۴۰۴ Oblivion Desktop

این نرم‌افزار طبق یک لایسنس خاص ارائه می‌شود.

<b>با استفاده از این نرم‌افزار یا دسترسی به منابع آن، شما موافقت می‌کنید که:</b>

- عدم امکان فروش: شما مجاز به فروش یا ارائه نسخه‌های تغییر یافته آن نیستید.
- محدودیت تغییرات: شما نمی‌توانید نام، صفحه درباره، اطلاعات کپی‌رایت، لوگو یا جزئیات بنیادین دیگر این نرم‌افزار را تغییر دهید. با این حال، توزیع نرم‌افزار به صورت باینری مجاز است، به شرطی که مجوز اصلی و اطلاعیه کپی‌رایت دست‌نخورده باقی بماند.

توسعه این نرم‌افزار تنها از طریق مشارکت در پروژه امکان‌پذیر است.


================================================
FILE: README-fa.md
================================================
<div align="center">
    <h1>Oblivion Desktop</h1>
</div>

<div align="center">
    <p>
        <a href="README.md">
            <small>English</small>
            <img src='assets/img/flags/gb.svg' alt='English' style='width: 20px;height: 15px;border-radius: 3px;' />
        </a>
        | 
       <img src='assets/img/flags/iran.svg' alt='Persian' style='width: 20px;height: 15px;border-radius: 3px;' />
        <small>فارسی</small>
    </p>
</div>

اوبلیویون دسترسی امن و بهینه به اینترنت را از طریق یک اپلیکیشن کاربرپسند برای ویندوز، مک و لینوکس با استفاده از فناوری
Cloudflare Warp فراهم می‌کند.

> نسخه‌ی غیررسمی از [Oblivion](https://github.com/bepass-org/oblivion)

<b>"اینترنت, برای همه یا هیچکس!"</b>

<p dir="rtl" align="center">
<a href="https://github.com/bepass-org/oblivion-desktop/releases/latest">
    <img src="https://img.shields.io/github/v/release/bepass-org/oblivion-desktop?label=Version&color=blue" alt="Version">
</a>
<a href="https://github.com/bepass-org/oblivion-desktop/releases/latest">
    <img src="https://img.shields.io/github/downloads/bepass-org/oblivion-desktop/total?label=Downloads" alt="Downloads">
</a>
<a href="https://github.com/bepass-org/oblivion-desktop">
    <img src="https://img.shields.io/github/stars/bepass-org/oblivion-desktop?style=flat&label=Stars&color=tomato" alt="Stars">
</a>
<a href="LICENSE.md">
    <img src="https://img.shields.io/badge/License-Restrictive-f84e29.svg?color=white" alt="License">
</a>
</p>

<p align="center">
    <img src="screenshot/oblivion.png" alt="oblivion.png">
</p>

## ویژگی‌ها

- **وی‌پی‌ان امن**: پیاده‌سازی اختصاصی WireGuard با زبان Go.
- **متن‌باز**: به‌صورت اپن‌سورس، با تأکید بر شفافیت و مشارکت اجتماعی؛ ضمن استفاده از گیت‌هاب اکشن برای بیلد خودکار.
- **کاربرپسند**: رابط کاربری ساده و آسان.

<p align="center">
    <img src="screenshot/oblivion.jpg" alt="oblivion.jpg">
</p>

## بررسی سریع

<div align="right">
<table>
    <thead align="right">
        <tr>
            <th>ویژگی</th>
            <th>وضعیت</th>
        </tr>
    </thead>
    <tbody align="right">
        <tr>
            <td>روش</td>
            <td>
                :white_check_mark:  وارپ<br>
                :white_check_mark:  گول<br>
                :white_check_mark:  سیفون <small>(سایفون)</small><br>
                :white_check_mark:  مسک
            </td>
        </tr>
        <tr>
            <td>پیکربندی شبکه</td>
            <td>
                :white_check_mark: پروکسی <small>(بدون تغییر)</small><br>
                :white_check_mark: پروکسی سیستم <small>(با PAC)</small><br>
                :white_check_mark: TUN <small>(با سینگ‌باکس)</small>
            </td>
        </tr>
        <tr>
            <td>قوانین مسیریابی</td>
            <td>
                :white_check_mark: پروکسی سیستم<br>
                :white_check_mark: دیتابیس Geo
            </td>
        </tr>
        <tr>
            <td>نوار سیستم</td>
            <td>
                :white_check_mark:  کمینه‌کردن<br>
                :white_check_mark: راه‌اندازی خودکار<br>
                :white_check_mark: میانبرها
            </td>
        </tr>
        <tr>
            <td>زبان‌ها</td>
            <td>
                :white_check_mark:  فارسی<br>
                :white_check_mark:  انگلیسی<br>
                :white_check_mark:  چینی<br>
                :white_check_mark:  روسی<br>
                :white_check_mark:  ترکی<br>
                :white_check_mark:  اندونزیایی <br>
                :white_check_mark:  عربی <br>
                :white_check_mark:  پرتغالی  <br>
                :white_check_mark:  ویتنامی <br>
                :white_check_mark:  اردو <br>
                :white_check_mark:  اسپانیایی <br>
                :white_check_mark:  برمه‌ای <br>
                :white_check_mark:  امهری
            </td>
        </tr>
        <tr>
            <td>پوسته</td>
            <td>
                :white_check_mark: روشن<br>
                :white_check_mark: تاریک<br>
                :white_check_mark: راست‌چین<br>
                :white_check_mark: چپ‌چین<br>
                :white_check_mark: خودکار
            </td>
        </tr>
        <tr>
            <td>سایر</td>
            <td>
                :white_check_mark: اسکنر<br>
                :white_check_mark: پینگ<br>
                :white_check_mark: دسترس‌پذیری<br>
                :white_check_mark: تست سرعت<br>
                :white_check_mark: بروزرسانی داخلی <small>(ویندوز)</small><br>
                :white_large_square: قطع اضطراری
            </td>
        </tr>
    </tbody>
</table>
</div>

## دانلود

<div align="right">
    <table>
        <thead align="right">
            <tr>
                <th>سیستم‌عامل / معماری</th>
                <th>سازگاری</th>
            </tr>
        </thead>
        <tbody align="right">
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-x64.exe"><img src="https://img.shields.io/badge/Windows-Setup x64-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-x64.zip"><img src="https://img.shields.io/badge/Windows-Portable x64-005AA8.svg?logo=gitforwindows"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-arm64.exe"><img src="https://img.shields.io/badge/Windows-Setup arm64-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-arm64.zip"><img src="https://img.shields.io/badge/Windows-Portable arm64-005AA8.svg?logo=gitforwindows"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-ia32.exe"><img src="https://img.shields.io/badge/Windows-Setup x86-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-ia32.zip"><img src="https://img.shields.io/badge/Windows-Portable x86-005AA8.svg?logo=gitforwindows"></a>
                </td>
                <td>
                    10+<br>
                </td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-arm64.dmg"><img src="https://img.shields.io/badge/macOS-DMG arm64-F0F0F1.svg?logo=apple"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-arm64.zip"><img src="https://img.shields.io/badge/macOS-ZIP arm64-9e9e9e.svg?logo=apple" /></a><br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-x64.dmg"><img src="https://img.shields.io/badge/macOS-DMG x64-F0F0F1.svg?logo=apple"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-x64.zip"><img src="https://img.shields.io/badge/macOS-ZIP x64-9e9e9e.svg?logo=apple" /></a>
                </td>
                <td>10.15+</td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-amd64.deb"><img src="https://img.shields.io/badge/GNU/Linux-DEB x64-DC470E.svg?logo=debian"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-x86_64.rpm"><img src="https://img.shields.io/badge/GNU/Linux-RPM x64-01ABD2.svg?logo=redhat"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-x64.tar.xz"><img src="https://img.shields.io/badge/GNU/Linux-tar.xz x64-EDC204.svg?logo=linux"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-x86_64.AppImage"><img src="https://img.shields.io/badge/GNU/Linux-AppImage x64-bf7645.svg?logo=linux"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-arm64.deb"><img src="https://img.shields.io/badge/GNU/Linux-DEB arm64-DC470E.svg?logo=debian"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-aarch64.rpm"><img src="https://img.shields.io/badge/GNU/Linux-RPM arm64-01ABD2.svg?logo=redhat"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-arm64.tar.xz"><img src="https://img.shields.io/badge/GNU/Linux-tar.xz arm64-EDC204.svg?logo=linux"></a>   
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-arm64.AppImage"><img src="https://img.shields.io/badge/GNU/Linux-AppImage arm64-bf7645.svg?logo=linux"></a>
                </td>
                <td>
                    گنوم (جی‌ستینگز)<br>
                    کی‌دی‌ای (کیو)<br>
                    جی‌لیبیسی (glibc)
                </td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion/releases/latest"><img src="https://img.shields.io/badge/Android-APK Universal-0d7365.svg?logo=android"></a>
                    <a href="https://play.google.com/store/apps/details?id=org.bepass.oblivion"><img src="https://img.shields.io/badge/Android-APK Universal-044d29.svg?logo=googleplay"></a>
                </td>
                <td>6+</td>
            </tr>
            <tr>
                <td>
                    <img src="https://img.shields.io/badge/iOS-Coming soon ...-pink.svg?logo=apple">
                </td>
                <td></td>
            </tr>
        </tbody>
    </table>
</div>

## به مشکل برخوردید 🐞؟

قسمت [ویکی](https://github.com/bepass-org/oblivion-desktop/wiki) را بررسی کرده و
در [مشکلات](https://github.com/bepass-org/oblivion-desktop/issues) (باز
و [بسته](https://github.com/bepass-org/oblivion-desktop/issues?q=is%3Aissue+is%3Aclosed)!) جستجو کنید؛ اگر پاسخ خود را
پیدا نکردید، [یک مشکل جدید](https://github.com/bepass-org/oblivion-desktop/issues/new/choose) گزارش کنید.

## مشارکت کنید

ما یک پروژه جامعه‌محور هستیم که هدف آن دسترسی همه به اینترنت است. چه بخواهید کد بزنید، چه ویژگی‌های جدید پیشنهاد دهید یا
نیاز به کمک داشته باشید، خوشحال می‌شویم از شما بشنویم!  
برای اطلاعات بیشتر
به [ایشوهای GitHub](https://github.com/bepass-org/oblivion-desktop/issues)، [راهنمای مشارکت](CONTRIBUTING.md)
و [مستندات توسعه‌دهندگان](DOCS.md) مراجعه کنید.

[![Stargazers over time](https://starchart.cc/bepass-org/oblivion-desktop.svg?variant=adaptive)](https://starchart.cc/bepass-org/oblivion-desktop)

## بیشتر بدانید

- [سوالات متداول](FAQ.md)
- [لاینسس](LICENSE.md)
- [امنیت](SECURITY.md)

![virustotal.jpg](screenshot/virustotal.jpg)

## تقدیر و تشکر

این پروژه بر شانه‌های اشخاصی ایستاده است و ما از کمک‌ها و الهام‌بخشی دوستان زیر به‌ شدت سپاسگزاریم:

- [Cloudflare Warp](https://www.cloudflare.com/application/terms/)
- [warp-plus](https://github.com/bepass-org/warp-plus/) (یوسف قبادی و مارک پشم‌فروش)
- [Oblivion](https://github.com/bepass-org/oblivion)
- [Oblivion Helper](https://github.com/ShadowZagrosDev/oblivion-helper) (GPLv3)
- [Masque-Plus](https://github.com/ircfspace/masque-plus)
- [Electron](https://www.electronjs.org/)
- [React](https://github.com/facebook/react)
- [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate)
- [electron-builder](https://github.com/electron-userland/electron-builder)
- [regedit](https://www.npmjs.com/package/regedit)
- [Iran Sing-box rules](https://github.com/Chocolate4U/Iran-sing-box-rules)
- [Shabnam Font](https://rastikerdar.github.io/shabnam-font/) (Saber Rastikerdar)
- [Zag-NetStats](https://github.com/ShadowZagrosDev/Zag-NetStats)
- [و دیگران 🧡](package.json)

## مشارکت‌کنندگان

ابلیویون دسکتاپ اینجاست، چون شما کمک کردین. دمتون گرم ✌️🧡 ([راهنمای مشارکت](CONTRIBUTING.md))

<img src="https://contrib.rocks/image?repo=bepass-org/oblivion-desktop" align="center" alt="Contributors" />


================================================
FILE: README.md
================================================
<div align="center">
    <h1>Oblivion Desktop</h1>
</div>

<div align="center">
    <p>
        <a href="README-fa.md">
            <small>فارسی</small>
            <img src='assets/img/flags/iran.svg' alt='persian' style='width: 20px;height: 15px;border-radius: 3px;' />
        </a>
        | 
       <img src='assets/img/flags/gb.svg' alt='English' style='width: 20px;height: 15px;border-radius: 3px;' />
        <small>English</small>
    </p>
</div>

Oblivion provides a secure, optimised internet access through a user-friendly Windows/Linux/MacOS app using Cloudflare WARP's
technology.

> Unofficial Desktop version of the mobile VPN: [Oblivion](https://github.com/bepass-org/oblivion)

<b>"Internet, for all or none!"</b>

[![Version](https://img.shields.io/github/v/release/bepass-org/oblivion-desktop?label=Version&color=blue)](https://github.com/bepass-org/oblivion-desktop/releases/latest)
[![Download](https://img.shields.io/github/downloads/bepass-org/oblivion-desktop/total?label=Downloads)](https://github.com/bepass-org/oblivion-desktop/releases/latest)
[![Stars](https://img.shields.io/github/stars/bepass-org/oblivion-desktop?style=flat&label=Stars&color=tomato
)](https://github.com/bepass-org/oblivion-desktop)
[![License](https://img.shields.io/badge/License-Restrictive-f84e29.svg?color=white)](LICENSE.md)

![oblivion.png](screenshot/oblivion.png)

## Features

- **High-grade VPN Security**: Custom WireGuard® implementation in Golang providing enterprise-level encryption with minimal overhead.
- **Free and Open Source**: Built with transparency & community contribution in mind, leveraging the power of GitHub Actions for automated builds.Community-driven development with transparent builds via GitHub Actions.
- **Intuitive Yet Powerful**: Clean interface designed for ease of use while offering advanced configuration options.
- **Modern Tech Stack**: TypeScript frontend with a high-performance Golang backend featuring our optimized WireGuard® implementation.

![oblivion.jpg](screenshot/oblivion.jpg)

## Overview

<div align=left>
<table>
    <thead align=left>
        <tr>
            <th>Feature</th>
            <th>Status</th>
        </tr>
    </thead>
    <tbody align=left>
        <tr>
            <td>Method</td>
            <td>
                :white_check_mark:  WARP<br>
                :white_check_mark:  Gool<br>
                :white_check_mark:  Cfon <small>(Psiphon)</small><br>
                :white_check_mark:  Masque
            </td>
        </tr>
        <tr>
            <td>Network configurations</td>
            <td>
                :white_check_mark: Proxy <small>(No changes)</small><br>
                :white_check_mark: System Proxy <small>(With PAC)</small><br>
                :white_check_mark: TUN <small>With Sing-Box</small>
            </td>
        </tr>
        <tr>
            <td>Routing rules</td>
            <td>
                :white_check_mark: System Proxy<br>
                :white_check_mark: GeoDB
            </td>
        </tr>
        <tr>
            <td>System tray</td>
            <td>
                :white_check_mark:  Minimize<br>
                :white_check_mark: BootUp<br>
                :white_check_mark: Shortcuts
            </td>
        </tr>
        <tr>
            <td>Languages</td>
            <td>
                :white_check_mark:  Persian (Farsi) <br>
                :white_check_mark:  English <br>
                :white_check_mark:  Chinese <br>
                :white_check_mark:  Russian <br>
                :white_check_mark:  Turkish <br>
                :white_check_mark:  Indonesian <br>
                :white_check_mark:  Arabic <br>
                :white_check_mark:  Portuguese <br>
                :white_check_mark:  Vietnamese <br>
                :white_check_mark:  Urdu <br>
                :white_check_mark:  Spanish <br>
                :white_check_mark:  Burmese <br>
                :white_check_mark:  Amharic
            </td>
        </tr>
        <tr>
            <td>Theme</td>
            <td>
                :white_check_mark: Light<br>
                :white_check_mark: Dark<br>
                :white_check_mark: RTL<br>
                :white_check_mark: LTR<br>
                :white_check_mark: Auto
            </td>
        </tr>
        <tr>
            <td>Other</td>
            <td>
                :white_check_mark: Scanner<br>
                :white_check_mark: Ping<br>
                :white_check_mark: Accessibility<br>
                :white_check_mark: SpeedTest<br>
                :white_check_mark: In-App Update <small>(Win)</small><br>
                :white_large_square: Kill Switch
            </td>
        </tr>
    </tbody>
    </table>
</div>

## Download

<div align=left>
    <table>
        <thead align="left">
            <tr>
                <th>OS / Arch</th>
                <th>Compatibility</th>
            </tr>
        </thead>
        <tbody align="left">
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-x64.exe"><img src="https://img.shields.io/badge/Windows-Setup x64-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-x64.zip"><img src="https://img.shields.io/badge/Windows-Portable x64-005AA8.svg?logo=gitforwindows"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-arm64.exe"><img src="https://img.shields.io/badge/Windows-Setup arm64-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-arm64.zip"><img src="https://img.shields.io/badge/Windows-Portable arm64-005AA8.svg?logo=gitforwindows"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-ia32.exe"><img src="https://img.shields.io/badge/Windows-Setup x86-0C88D8.svg?logo=gitforwindows"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-win-ia32.zip"><img src="https://img.shields.io/badge/Windows-Portable x86-005AA8.svg?logo=gitforwindows"></a>
                </td>
                <td>
                    10+<br>
                </td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-arm64.dmg"><img src="https://img.shields.io/badge/macOS-DMG arm64-F0F0F1.svg?logo=apple"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-arm64.zip"><img src="https://img.shields.io/badge/macOS-ZIP arm64-9e9e9e.svg?logo=apple" /></a><br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-x64.dmg"><img src="https://img.shields.io/badge/macOS-DMG x64-F0F0F1.svg?logo=apple"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-mac-x64.zip"><img src="https://img.shields.io/badge/macOS-ZIP x64-9e9e9e.svg?logo=apple" /></a>
                </td>
                <td>10.15+</td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-amd64.deb"><img src="https://img.shields.io/badge/GNU/Linux-DEB x64-DC470E.svg?logo=debian"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-x86_64.rpm"><img src="https://img.shields.io/badge/GNU/Linux-RPM x64-01ABD2.svg?logo=redhat"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-x64.tar.xz"><img src="https://img.shields.io/badge/GNU/Linux-tar.xz x64-EDC204.svg?logo=linux"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-x86_64.AppImage"><img src="https://img.shields.io/badge/GNU/Linux-AppImage x64-bf7645.svg?logo=linux"></a>
                    <br>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-arm64.deb"><img src="https://img.shields.io/badge/GNU/Linux-DEB arm64-DC470E.svg?logo=debian"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-aarch64.rpm"><img src="https://img.shields.io/badge/GNU/Linux-RPM arm64-01ABD2.svg?logo=redhat"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-arm64.tar.xz"><img src="https://img.shields.io/badge/GNU/Linux-tar.xz arm64-EDC204.svg?logo=linux"></a>
                    <a href="https://github.com/bepass-org/oblivion-desktop/releases/latest/download/oblivion-desktop-linux-arm64.AppImage"><img src="https://img.shields.io/badge/GNU/Linux-AppImage arm64-bf7645.svg?logo=linux"></a>
                </td>
                <td>
                    Gnome (gsettings)<br>
                    KDE (kio)<br>
                    GNU/Linux (glibc)
                </td>
            </tr>
            <tr>
                <td>
                    <a href="https://github.com/bepass-org/oblivion/releases/latest"><img src="https://img.shields.io/badge/Android-APK Universal-0d7365.svg?logo=android"></a>
                    <a href="https://play.google.com/store/apps/details?id=org.bepass.oblivion"><img src="https://img.shields.io/badge/Android-APK Universal-044d29.svg?logo=googleplay"></a>
                </td>
                <td>6+</td>
            </tr>
            <tr>
                <td>
                    <img src="https://img.shields.io/badge/iOS-Coming soon...-pink.svg?logo=apple">
                </td>
                <td></td>
            </tr>
        </tbody>
    </table>
</div>

## Faced a bug? 🐛

Take a look at our comprehensive [wiki](https://github.com/bepass-org/oblivion-desktop/wiki) and search
in [issues](https://github.com/bepass-org/oblivion-desktop/issues) (open
and [closed](https://github.com/bepass-org/oblivion-desktop/issues?q=is%3Aissue+is%3Aclosed) ones!) and if you did not
find your answer, then [create a new issue](https://github.com/bepass-org/oblivion-desktop/issues/new/choose)!

## Get involved!

We're a community-driven project, aiming to make the internet accessible for all. Whether you want to contribute code,
suggest features, or need some help, we'd love to hear from you!
Check out
our [GitHub Issues](https://github.com/bepass-org/oblivion-desktop/issues), [Contribution Guide](CONTRIBUTING.md)
and [Developer Docs](DOCS.md).

[![Stargazers over time](https://starchart.cc/bepass-org/oblivion-desktop.svg?variant=adaptive)](https://starchart.cc/bepass-org/oblivion-desktop)

## Read more...

* [FAQ.md](FAQ.md)
* [License.md](LICENSE.md)
* [SECURITY.md](SECURITY.md)

![virustotal.jpg](screenshot/virustotal.jpg)

## Acknowledgements

This project owes its existence to the groundbreaking work of others. We extend our sincere gratitude to the following contributors and innovators whose work inspired and enabled our efforts:

- [Cloudflare Warp](https://www.cloudflare.com/application/terms/)
- [warp-plus](https://github.com/bepass-org/warp-plus/) (Yousef Ghobadi & Mark Pashmfouroush)
- [Oblivion](https://github.com/bepass-org/oblivion)
- [Oblivion Helper](https://github.com/ShadowZagrosDev/oblivion-helper) (GPLv3)
- [Masque-Plus](https://github.com/ircfspace/masque-plus)
- [Electron](https://www.electronjs.org/)
- [React](https://github.com/facebook/react)
- [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate)
- [electron-builder](https://github.com/electron-userland/electron-builder)
- [regedit](https://www.npmjs.com/package/regedit)
- [Iran Sing-Box Rules](https://github.com/Chocolate4U/Iran-sing-box-rules)
- [Shabnam Font](https://rastikerdar.github.io/shabnam-font/) (Saber Rastikerdar)
- [Zag-NetStats](https://github.com/ShadowZagrosDev/Zag-NetStats)
- [And others! 🧡](package.json)

## Contributors

Oblivion Desktop made possible by contributors of Bepass and you! ✌️
We all appreciate your help and support. Here is a comprehensive contribution guide: ([Contribution Guide](CONTRIBUTING.md))

<img src="https://contrib.rocks/image?repo=bepass-org/oblivion-desktop" align="center" alt="Contributors" />


================================================
FILE: SECURITY.md
================================================
# امنیت برنامه

اپ متن‌باز Oblivion به‌عنوان یکی‌از امن‌ترین و مطمئن‌ترین VPNها، در روزهای پراختلال اخیر نقش مهمی برای دسترسی آزاد
کاربران ایرانی به اینترنت ایفا کرده.

- اپ‌های Oblivion و Oblivion Deskop و همینطور هسته Warp-Plus متن‌باز هستن و سورسشون در گیت‌هاب قرار گرفته، ضمن اینکه
  بیلد پروژه‌ها به‌صورت خودکار توسط گیت‌هاب اکشن انجام می‌شن.
- وارپ به‌وسیله رمزنگاری مقدار بیشتری از ترافیک خروجی از دستگاهتون، اجازه نمیده هیچ‌کس در کار شما سرک بکشه.
- نتیجه بررسی برنامه در VirusTotal فاقد هرگونه ایراد بوده است.

![virustotal.jpg](screenshot/virustotal.jpg)

# Application Security

The open-source Oblivion application, recognized as one of the most secure and reliable VPN providers, has played a crucial role in providing unrestricted internet access for Iranian users during recent disruptions going on in Iran.

- The Oblivion and Oblivion Desktop programs, as well as the WARP-Plus core, are open-source and their source code is available on their respective GitHub repositories. Additionally, project builds are automatically handled by GitHub Actions.
- WARP uses encryption to protect a larger portion of your device's outgoing traffic, ensuring that no one can monitor your activities.
- The program's results on VirusTotal have shown no issues.


================================================
FILE: assets/assets.d.ts
================================================
type Styles = Record<string, string>;

declare module '*.svg' {
  import React = require('react');

  export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;

  const content: string;
  export default content;
}

declare module '*.png' {
  const content: string;
  export default content;
}

declare module '*.jpg' {
  const content: string;
  export default content;
}

declare module '*.scss' {
  const content: Styles;
  export default content;
}

declare module '*.sass' {
  const content: Styles;
  export default content;
}

declare module '*.css' {
  const content: Styles;
  export default content;
}


================================================
FILE: assets/css/materialIcons.css
================================================
@font-face {
    font-family: 'Material Icons';
    font-style: normal;
    font-weight: 400;
    src: url('../font/material/MaterialIcons-Regular.woff2') format('woff2');
}

.material-icons {
    font-family: 'Material Icons';
    font-weight: normal;
    font-style: normal;
    font-size: 24px;
    display: inline-block;
    line-height: 1;
    text-transform: none;
    letter-spacing: normal;
    word-wrap: normal;
    white-space: nowrap;
    direction: ltr;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    -moz-osx-font-smoothing: grayscale;
    font-feature-settings: 'liga';
}

.material-icons.md-18 {
    font-size: 18px;
}
.material-icons.md-24 {
    font-size: 24px;
}
.material-icons.md-28 {
    font-size: 28px;
}
.material-icons.md-36 {
    font-size: 36px;
}
.material-icons.md-48 {
    font-size: 48px;
}

.material-icons.md-dark {
    color: rgba(0, 0, 0, 0.54);
}
.material-icons.md-dark.md-inactive {
    color: rgba(0, 0, 0, 0.26);
}
.material-icons.md-light {
    color: rgba(255, 255, 255, 1);
}
.material-icons.md-light.md-inactive {
    color: rgba(255, 255, 255, 0.3);
}


================================================
FILE: assets/css/noto.css
================================================
@font-face {
    font-family: 'Noto Color Emoji';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../font/noto/NotoColorEmoji-Regular0.woff2') format('woff2');
    unicode-range: U+1f1e6-1f1ff;
}


================================================
FILE: assets/css/shabnam.css
================================================
@font-face {
    font-family: shabnam;
    src: url('../font/shabnam/Shabnam-Thin.eot');
    src:
        url('../font/shabnam/Shabnam-Thin.eot?#iefix') format('embedded-opentype'),
        url('../font/shabnam/Shabnam-Thin.woff') format('woff'),
        url('../font/shabnam/Shabnam-Thin.ttf') format('truetype');
    font-weight: 100;
}

@font-face {
    font-family: shabnam;
    src: url('../font/shabnam/Shabnam-Light.eot');
    src:
        url('../font/shabnam/Shabnam-Light.eot?#iefix') format('embedded-opentype'),
        url('../font/shabnam/Shabnam-Light.woff') format('woff'),
        url('../font/shabnam/Shabnam-Light.ttf') format('truetype');
    font-weight: 200;
}

@font-face {
    font-family: shabnam;
    src: url('../font/shabnam/Shabnam.eot');
    src:
        url('../font/shabnam/Shabnam.eot?#iefix') format('embedded-opentype'),
        url('../font/shabnam/Shabnam.woff') format('woff'),
        url('../font/shabnam/Shabnam.ttf') format('truetype');
    font-weight: 300;
}

@font-face {
    font-family: shabnam;
    src: url('../font/shabnam/Shabnam-Medium.eot');
    src:
        url('../font/shabnam/Shabnam-Medium.eot?#iefix') format('embedded-opentype'),
        url('../font/shabnam/Shabnam-Medium.woff') format('woff'),
        url('../font/shabnam/Shabnam-Medium.ttf') format('truetype');
    font-weight: 400;
}

@font-face {
    font-family: shabnamDigits;
    src: url('../font/shabnam/Shabnam-Medium-FD-WOL.eot');
    src:
        url('../font/shabnam/Shabnam-Medium-FD-WOL.eot?#iefix') format('embedded-opentype'),
        url('../font/shabnam/Shabnam-Medium-FD-WOL.woff') format('woff'),
        url('../font/shabnam/Shabnam-Medium-FD-WOL.ttf') format('truetype');
    font-weight: 400;
}

@font-face {
    font-family: shabnam;
    src: url('../font/shabnam/Shabnam-Bold.eot');
    src:
        url('../font/shabnam/Shabnam-Bold.eot?#iefix') format('embedded-opentype'),
        url('../font/shabnam/Shabnam-Bold.woff') format('woff'),
        url('../font/shabnam/Shabnam-Bold.ttf') format('truetype');
    font-weight: 900;
}


================================================
FILE: assets/css/style.css
================================================
body {
    font: 300 17px shabnam;
    background: #fff;
    color: #4c4b4b;
    line-height: 20px;
    word-wrap: break-word;
    position: relative;
    transition: all 0.2s ease-in-out;
}

[dir='ltr'] body {
    text-align: left;
    direction: ltr;
}

[data-bs-theme='dark'] body {
    background: #0a0e24;
    color: #dcdcdc;
}

body:before {
    height: 40vh;
    background: linear-gradient(#ffde06 0%, rgba(128, 128, 128, 0) 100%);
    opacity: 0.2;
    content: '';
    float: right;
    width: 100%;
    position: absolute;
    transition: all 0.2s ease-in-out;
}

[data-bs-theme='dark'] body:after {
    background: linear-gradient(#ffde06 0%, rgba(128, 128, 128, 0) 100%);
}

.dirLeft {
    direction: ltr;
    text-align: left;
}

.dirLeft[dir='rtl'] {
    direction: rtl;
}

.dirRight {
    direction: rtl;
    text-align: right;
}

nav.header {
    float: right;
    width: 100%;
    position: fixed;
    top: 0;
    right: 0;
    padding: 20px 0;
    transition: all 0.2s ease-in-out;
    z-index: 2;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

nav.isSticky {
    background: rgba(253, 248, 209, 0.9);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
    backdrop-filter: blur(5px);
    --webkit-backdrop-filter: blur(5px);
}

[data-bs-theme='dark'] nav.isSticky {
    background: rgba(53, 50, 33, 0.9);
}

nav.header i {
    color: #676767;
    font-size: 30px !important;
    transition: all 0.2s ease-in-out;
    cursor: pointer;
}

[data-bs-theme='dark'] nav.header i {
    color: #dcdcdc;
}

nav.header i:hover {
    color: #4f4f4f;
}

[data-bs-theme='dark'] nav.header i:hover {
    color: #eaeaea;
}

nav.header i.navPaste {
    float: left;
    margin: 2px 0 0 15px;
    font-size: 26px !important;
}

[dir='ltr'] nav.header i.navPaste {
    float: right;
    margin: 2px 15px 0 0;
}

nav.header i.log {
    color: #a1a1a1;
    margin: 0 0 0 15px;
    font-size: 31px !important;
    float: left;
}

[dir='ltr'] nav.header i.log {
    float: right;
    margin: 0 15px 0 0;
}

nav.header i.navLeft {
    float: left;
}

[dir='ltr'] nav.header i.navLeft {
    float: right;
}

nav.header i.log:hover {
    color: #e15959;
}

[data-bs-theme='dark'] nav.header i.log:hover {
    color: #ed6a6a;
}

nav.header a.disabled {
    pointer-events: none;
}

nav.header h3 {
    font-size: 19px;
    font-weight: 200;
    color: #303030;
    margin: 4px 0 0 0;
    float: right;
}

[dir='ltr'] nav.header h3 {
    float: left;
    margin: 4px 0 0 0;
}

[data-bs-theme='dark'] nav.header h3 {
    color: #dcdcdc;
}

nav.header i.backBtn {
    font-size: 30px;
    float: left;
    margin-left: -5px;
}

[dir='ltr'] nav.header i.backBtn {
    float: right;
    rotate: 180deg;
    margin-left: 0;
    margin-right: -5px;
}

nav.header i.backBtn:hover {
    color: #1058ff;
}

nav.header .navMenu {
    position: relative;
    float: right;
    transition: all 0.2s ease-in-out;
}

[dir='ltr'] nav.header .navMenu {
    float: left;
}

nav.header .navMenu .indicator {
    position: absolute;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #ffa200;
    top: 1px;
    left: -3px;
    animation: indicator 1s linear infinite;
}

@keyframes indicator {
    0% {
        opacity: 1;
    }
    50% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

::-webkit-scrollbar {
    background: #fff;
    width: 7px;
    height: 3px;
}

::-webkit-scrollbar-thumb {
    background: rgba(253, 248, 209, 0.9);
}

::-webkit-scrollbar-thumb:hover {
    background: rgb(236, 230, 184);
}

[data-bs-theme='dark'] ::-webkit-scrollbar {
    background: #212121;
}

[data-bs-theme='dark'] ::-webkit-scrollbar-thumb {
    background: rgba(53, 50, 33, 0.9);
}

[data-bs-theme='dark'] ::-webkit-scrollbar-thumb:hover {
    background: rgb(83, 78, 54);
}

.container {
    max-width: 400px;
}

.myApp {
    float: right;
    width: 100%;
}

.myApp.normalPage {
    margin: 80px 0 25px 0;
    z-index: 1;
    position: relative;
}
.myApp.normalPage.withScroll {
    margin: 80px 0;
}

.myApp.normalPage p {
    float: right;
    width: 100%;
    line-height: 26px;
    font-weight: 300;
    font-size: 15px;
}

.verticalAlign {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 80vh;
}

.myApp .iframe {
    float: right;
    width: 100%;
    position: relative;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.myApp .iframe:after {
    float: right;
    width: 100%;
    position: absolute;
    content: '';
    top: 0;
    left: 0;
    height: 300px;
    background: transparent;
    z-index: 1;
}

.myApp .iframe iframe {
    float: right;
    width: 100%;
    height: calc(100vh - 200px);
    border: none;
    border-radius: 250px 250px 25px 25px;
}

.homeScreen {
    float: right;
    width: 100%;
    margin-top: 150px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-evenly;
    flex-wrap: nowrap;
    align-content: center;
    user-select: none;
    position: relative;
}

.homeScreen .title {
    float: right;
    width: 227px;
}

.homeScreen .title h1 {
    float: right;
    width: 100%;
    color: #707070;
    font-size: 50px;
    font-weight: 900;
    text-align: center;
    margin: 0;
    padding: 0;
}

[data-bs-theme='dark'] .homeScreen .title h1 {
    color: #c5c5c5;
}

.homeScreen .title h2 {
    float: right;
    width: 100%;
    padding: 0;
    color: #ffa200;
    font-size: 25px;
    font-weight: 300;
    text-align: right;
    margin: 0;
}

[data-bs-theme='dark'] .homeScreen .title h2 {
    color: #ffa200;
}

[dir='ltr'] .homeScreen .title h2 {
    text-align: left;
}

.homeScreen .title h2 .badge {
    font-weight: 100;
    margin: 6px 3px 0 3px;
    font-size: 12px;
    line-height: 12px;
    padding: 4px 5px 2px 5px;
    background: rgba(203, 203, 203, 0.3);
    transition: all 0.2s ease-in-out;
    color: #858585;
    float: left;
    opacity: 0;
}
.homeScreen .title:hover h2 .badge {
    opacity: 1;
}
[data-bs-theme='dark'] .homeScreen .title h2 .badge {
    background: rgba(107, 107, 107, 0.3);
    color: #a5a5a5;
}
[dir='ltr'] .homeScreen .title h2 .badge {
    float: right;
    margin: 3px 3px 0 3px;
}

@media screen and (min-width: 600px) {
    .homeScreen .title h2 {
        text-align: center;
    }
}

.homeScreen form {
    float: right;
    width: 100%;
}

.homeScreen form .connector {
    margin: auto;
    float: right;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}

.homeScreen form .connector .switch {
    border-radius: 50px;
    background: #d9d9d9;
    width: 175px;
    height: 75px;
    margin: 100px 0 50px 0;
    box-shadow: 0 0 15px rgba(0, 0, 0, 0.11);
    float: right;
    position: relative;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
    border: none;
}

[data-bs-theme='dark'] .homeScreen form .connector .switch {
    background: #333643;
}

.homeScreen form .connector .switch.active {
    background: #ffad0a;
}

.homeScreen form .connector .switch .circle {
    width: 65px;
    height: 65px;
    background: #fff;
    position: absolute;
    left: 5px;
    top: 5px;
    border-radius: 50%;
    transition: all 0.2s ease-in-out;
}

.homeScreen form .connector .switch.active .circle {
    left: calc(100% - 70px);
}

.homeScreen form .connector .switch.isLoading {
    cursor: wait;
}

.homeScreen form .connector .switch.isLoading .circle .spinner {
    animation: spinner 0.8s linear infinite;
    border: 4px solid #9b9b9b;
    border-right-color: transparent;
    border-radius: 100%;
    display: inline-block;
    position: relative;
    overflow: hidden;
    text-indent: -9999px;
    vertical-align: middle;
    margin: 5px;
    width: 55px;
    height: 55px;
}

@keyframes spinner {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(1turn);
    }
}

.homeScreen .status {
    width: 100%;
    float: right;
    text-align: center;
    color: #4b4b4b;
}

[data-bs-theme='dark'] .homeScreen .status {
    color: #dcdcdc;
}

.homeScreen .status.active {
    color: #63bc0a;
}

.homeScreen .ip {
    color: #aeaeae;
    margin: 10px 0 0 0;
}

.homeScreen .ip img,
.homeScreen .ip svg {
    width: 17px;
    height: 12px;
    border-radius: 3px;
    margin: 0 0 0 7px;
    object-fit: cover;
    transition: all 0.2s ease-in-out;
    opacity: 0;
}

.homeScreen .ip.connected img,
.homeScreen .ip.connected svg {
    opacity: 1;
}

.homeScreen .ip span {
    opacity: 0;
    transition: all 0.2s ease-in-out;
}

.homeScreen .ip span:not(:empty) {
    cursor: pointer;
}

.homeScreen .ip.connected span {
    opacity: 1;
}

.homeScreen .inFoot {
    position: fixed;
    bottom: -90px;
    min-width: 200px;
    max-width: 80%;
    background: #f1f1f1;
    border-radius: 15px 15px 0 0;
    padding: 10px;
    line-height: 15px;
    text-align: center;
    transition: all 0.5s ease-in-out;
    opacity: 0;
    left: 50%;
    transform: translateX(-50%);
    max-width: 300px;
}

.homeScreen .inFoot.withIp {
    min-width: 230px;
}

.homeScreen .inFoot.active {
    opacity: 1;
    bottom: -5px;
}

html[data-bs-theme='dark'] .homeScreen .inFoot {
    background: #171c2b;
}

.homeScreen .inFoot small {
    text-align: center;
    font-weight: 200;
    opacity: 0;
}

html[data-bs-theme='dark'] .homeScreen .inFoot small {
    font-weight: 100;
}

.homeScreen .inFoot small {
    opacity: 0.8;
}

.homeScreen .inFoot small:not(:empty) {
    cursor: pointer;
}

.homeScreen .inFoot .item {
    float: left;
    width: 100%;
    margin: 5px 0 6px 0;
}
.homeScreen .inFoot .item img {
    float: left;
    width: 17px;
    height: 12px;
    border-radius: 3px;
    margin: 1px 0 0 7px;
    object-fit: cover;
}
.homeScreen .inFoot .item img[src*="flagsapi"] {
    object-fit: none;
}

.homeScreen .inFoot .item img[alt='false Flag'],
.homeScreen .inFoot .item img[alt='xx Flag'] {
    filter: brightness(77%);
}

html[data-bs-theme='dark'] .homeScreen .inFoot .item img[alt='false Flag'],
html[data-bs-theme='dark'] .homeScreen .inFoot .item img[alt='xx Flag'] {
    filter: brightness(33%);
}

.homeScreen .inFoot .item i {
    float: left;
    margin: -4px -1px 0 5px;
    font-size: 20px;
    opacity: 0.5;
}

@keyframes placeHolderShimmer {
    0% {
        background-position: -468px 0;
    }
    100% {
        background-position: 468px 0;
    }
}

.shimmer {
    -webkit-animation-duration: 3s;
    animation-duration: 3s;
    -webkit-animation-fill-mode: forwards;
    animation-fill-mode: forwards;
    -webkit-animation-iteration-count: infinite;
    animation-iteration-count: infinite;
    -webkit-animation-name: placeHolderShimmer;
    animation-name: placeHolderShimmer;
    -webkit-animation-timing-function: linear;
    animation-timing-function: linear;
    background: #bebebe;
    background: linear-gradient(to left, #dddddd 8%, #bebebe 18%, #dddddd 33%);
    background-size: 800px 104px;
    position: relative;
    color: transparent !important;
    border-radius: 25px;
    height: 15px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

html[data-bs-theme='dark'] .shimmer {
    background: linear-gradient(to left, #0a0e24 8%, #171c2b 18%, #0a0e24 33%);
    background-size: 800px 104px;
}

.homeScreen .inFoot .item span {
    float: left;
    margin-left: 10px;
    min-width: 150px;
    width: calc(100% - 43px);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    direction: ltr;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}

.homeScreen .inFoot .item.ping span {
    opacity: 0.8;
    font-size: 13px;
    height: 13px;
    min-width: auto;
    width: auto;
}

.homeScreen .inFoot .item.speed {
    margin: 1px 0 5px 0;
    cursor: default;
}
.homeScreen .inFoot .item.speed .download {
    float: left;
}
.homeScreen .inFoot .item.speed .upload {
    float: right;
    margin-right: 4px;
}
.homeScreen .inFoot .item.speed .download i,
.homeScreen .inFoot .item.speed .upload i {
    cursor: default;
    font-size: 17px;
    margin: -3px 0px 0 6px;
}
.homeScreen .inFoot .item.speed .download span,
.homeScreen .inFoot .item.speed .upload span {
    opacity: 0.8;
    font-size: 13px;
    height: 13px;
    min-width: auto;
    width: auto;
    cursor: default;
}
.homeScreen .inFoot .item.speed .isPing span {
    cursor: pointer;
}
.homeScreen .inFoot .item.speed .download span small,
.homeScreen .inFoot .item.speed .upload span small {
    cursor: default;
}

.homeScreen .inFoot .item .hasTooltip {
    position: relative;
}
.homeScreen .inFoot .item .hasTooltip .isTooltip {
    position: absolute;
    content: '';
    background: #fff;
    border-radius: 5px;
    color: #000;
    min-width: 105px;
    padding: 7px;
    font-size: 12px;
    font-weight: 200;
    line-height: 17px;
    bottom: 20px;
    right: -3px;
    transition: all 0.2s ease-in-out;
    box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.1);
    opacity: 0;
}
[data-bs-theme='dark'] .homeScreen .inFoot .item .hasTooltip .isTooltip {
    background: #01030a;
    color: #c3c3c3;
}
.homeScreen .inFoot .item .hasTooltip:hover .isTooltip {
    opacity: 1;
}
.homeScreen .inFoot .item .hasTooltip .isTooltip i {
    float: left;
    font-style: normal;
    opacity: 0.3;
    position: absolute;
    left: 5px;
    top: 5px;
    margin: 0;
}
.homeScreen .inFoot .item .hasTooltip .isTooltip i.latest {
    top: inherit;
    bottom: 6px;
}
.homeScreen .inFoot .item .hasTooltip .isTooltip span {
    direction: ltr;
    float: right;
    width: auto;
    text-align: left;
    overflow: inherit;
    white-space: inherit;
    text-overflow: inherit;
    margin: 0;
}
.homeScreen .inFoot .item .hasTooltip .isTooltip .clearfix {
    float: left;
    width: 100%;
    height: 4px;
}

.logPage .logText {
    font-size: 14px !important;
    line-height: 24px !important;
    white-space: pre-line;
}

[dir='ltr'] .logPage .logText {
    direction: ltr;
    text-align: left;
}

.logPage .logOptions {
    float: left;
    margin: 0;
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: rgba(253, 248, 209, 0.9);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
    padding: 10px 15px;
    border-radius: 25px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[data-bs-theme='dark'] .logPage .logOptions {
    background: rgba(53, 50, 33, 0.9);
}

.logPage .logOptions div[role] {
    float: left;
    margin-right: 10px;
}

.logPage .logOptions div[role]:last-child {
    margin-right: 0;
}

.logPage .logOptions i {
    float: left;
    font-size: 20px;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
}

.logPage .logOptions i:hover {
    opacity: 0.7;
}

.settings {
    float: right;
    width: 100%;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.settings .lottie {
    display: flex;
    justify-content: center;
    align-items: center;
    float: right;
    width: 100%;
    height: 100vh;
}

.settings .grouped {
    float: right;
    width: 100%;
}

.settings .grouped .item {
    border-bottom: none;
    padding-bottom: 0;
}

.settings .grouped .item:last-child {
    border-bottom: 1px solid #dcdcdc;
    padding-bottom: 15px;
}

.settings .item {
    float: right;
    width: 100%;
    border-bottom: 1px solid #dcdcdc;
    transition: all 0.2s ease-in-out;
    padding: 14px 0 15px 0;
}

[data-bs-theme='dark'] .settings .item,
[data-bs-theme='dark'] .settings .grouped .item:last-child {
    border-color: #2b2e3b;
}

.settings .item .loader {
    float: left;
    width: 10px;
    aspect-ratio: 1;
    border-radius: 50%;
    background:
        radial-gradient(farthest-side, #ffa516 94%, #0000) top/2px 2px no-repeat,
        conic-gradient(#0000 30%, #ffa516);
    mask: radial-gradient(farthest-side, #0000 calc(100% - 2px), #000 0);
    animation: loaderAnim 1s infinite linear;
    margin: 8px 0 0 7px;
}
[dir='ltr'] .settings .item .loader {
    float: right;
    margin: 8px 7px 0 0;
}
@keyframes loaderAnim {
    100% {
        transform: rotate(1turn);
    }
}

.settings .item.disabled {
    opacity: 0.3;
}

.settings .item.highlight {
    background: #fffa9b;
}

[data-bs-theme='dark'] .settings .item.highlight {
    background: #2d2e53;
}

.settings .item:first-child {
    padding-top: 0;
}

.settings .item:last-child {
    border-bottom: none;
}

.settings .item .key {
    float: right;
    width: 40%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 22px;
    font-weight: 300;
    color: #303030;
    margin: 3px 0 0 0;
    padding: 2px 0;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[dir='ltr'] .settings .item .key {
    float: left;
}

[data-bs-theme='dark'] .settings .item .key {
    color: #e8e8e8;
}

.settings .item .value {
    float: left;
    margin: 0;
    width: 60%;
    padding-top: 1px;
}

[dir='ltr'] .settings .item .value {
    float: right;
}

.settings .item .value span {
    float: left;
    color: #ffa200;
    margin: 6px 0 0 0;
    font-size: 22px;
    font-weight: 300;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    width: 100%;
    cursor: pointer;
    padding-top: 1px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[dir='ltr'] .settings .item .value span {
    float: right;
    text-align: right;
}

.settings .item.disabled .value span {
    cursor: default;
}

.settings .item .value span[dir='auto'] {
    direction: rtl;
}

[dir='ltr'] .settings .item .value span[dir='auto'] {
    direction: ltr;
}

.settings .item .value select {
    float: left;
    border: none;
    background: none;
    direction: ltr;
    font-size: 22px;
    font-weight: 300;
    color: #ffa200;
    cursor: pointer;
    appearance: none;
    line-height: 24px;
    padding: 0;
    margin: 4px 0 -2px 0;
    font-family: 'Noto Color Emoji', 'shabnam';
}

[dir='ltr'] .settings .item .value select {
    float: right;
    text-align: right;
}

.settings .item.disabled .value select {
    cursor: default;
}

.settings .item .value select option {
    direction: ltr;
    font-size: 18px;
    font-weight: 300;
    color: #666666;
}

.settings .item .value i {
    float: left;
    font-size: 28px;
    cursor: pointer;
    color: #ffa200;
}

[dir='ltr'] .settings .item .value i {
    float: right;
}

.settings .item .value .checkbox {
    float: left;
    border: 3px solid #a1a1a1;
    background: transparent;
    width: 27px;
    height: 27px;
    border-radius: 4px;
    padding: 0;
    margin: 2px 0 -1px 0;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
}

[dir='ltr'] .settings .item .value .checkbox {
    float: right;
}

[data-bs-theme='dark'] .settings .item .value .checkbox {
    border-color: #dcdcdc;
}

.settings .item.disabled .value .checkbox,
.settings .item.disabled .value .checkbox i {
    cursor: default;
}

.settings .item .value .checkbox.checked {
    background: #ffa200;
    border-color: #ffa200;
}

.settings .item .value .checkbox i {
    float: left;
    color: #fff;
    font-size: 27px;
    margin: -3px 0 0 -3px;
    opacity: 0;
    transition: all 0.2s ease-in-out;
    rotate: -25deg;
}

.settings .item .value .checkbox.checked i {
    opacity: 1;
    rotate: 0deg;
}

.settings .item .value .switch {
    float: left;
    border: 3px solid #a1a1a1;
    background: transparent;
    width: 27px;
    height: 27px;
    border-radius: 50%;
    padding: 0;
    margin: 2px 0 -1px 0;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
}

[dir='ltr'] .settings .item .value .switch {
    float: right;
}

[data-bs-theme='dark'] .settings .item .value .switch {
    border-color: #dcdcdc;
}

.settings .item .value .switch.checked {
    position: relative;
    border-color: #ffa200;
}

.settings .item .value .switch.checked:after {
    position: absolute;
    content: '';
    top: 5px;
    right: 5px;
    width: 11px;
    height: 11px;
    background: #ffa200;
    border-radius: 50%;
}

.settings .item .info {
    float: right;
    width: 100%;
    color: #717171;
    font-size: 14px;
    margin: 5px 0 0 0;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[dir='ltr'] .settings .item .info {
    float: left;
}

[data-bs-theme='dark'] .settings .item .info {
    color: #a0a0a0;
    font-weight: 200;
}

.settings .item[role='button'],
.settings .item[role='button'].disabled {
    cursor: default;
}

.moreSettings {
    float: right;
    width: 100%;
    padding: 0;
    margin: 30px 0 20px 0;
    font-size: 15px;
    opacity: 0.5;
    font-weight: 200;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[dir='ltr'] .moreSettings {
    float: left;
}

.moreSettings i {
    float: right;
    margin: -2px -8px 0 5px;
    opacity: 0.5;
}

[dir='ltr'] .moreSettings i {
    float: left;
    margin: -3px 5px 0 -8px;
}

.appToast {
    float: right;
    width: 100%;
    padding: 0;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    margin-bottom: 10px;
}
.appToast.inModal {
    margin: 10px 0 0 0;
    padding: 0;
}

.appToast div {
    float: right;
    width: 100%;
    padding: 10px;
    border-radius: 5px;
    font-size: 14px;
    font-weight: 200;
    line-height: 24px;
    background: #f1f1f1;
}

[data-bs-theme='dark'] .appToast div {
    background: #2a273f;
}

.appToast div i {
    float: right;
    height: 100%;
    padding: 10px 0 10px 10px;
    opacity: 0.3;
}

[dir='ltr'] .appToast div i {
    float: left;
    padding: 10px 10px 10px 0;
}

.toastHelp {
    float: left;
    border: none;
    font-size: 13px;
    font-weight: 200;
    transition: all 0.2s ease-in-out;
    opacity: 0.7;
    padding: 2px 7px;
    line-height: 18px;
    background: #1e1e1e;
    color: #fff;
    border-radius: 4px;
    margin: -1px 10px 0 0;
}
[dir='ltr'] .toastHelp {
    float: right;
    margin: -1px 0 0 10px;
}
.toastHelp:focus,
.toastHelp:hover {
    opacity: 1;
    text-decoration: none;
    color: orange;
}

.splashScreen {
    float: left;
    width: 100%;
    background: #fff;
    color: #4c4b4b;
    z-index: 3;
    position: absolute;
    height: 100vh;
}

[data-bs-theme='dark'] .splashScreen {
    background-color: #0a0e24;
    color: #dcdcdc;
}

.splashScreen:before {
    height: 40vh;
    background: linear-gradient(#ffde06 0%, rgba(128, 128, 128, 0) 100%);
    opacity: 0.2;
    content: '';
    float: right;
    width: 100%;
    position: absolute;
    transition: all 0.2s ease-in-out;
}

[data-bs-theme='dark'] .splashScreen:after {
    background: linear-gradient(#ffde06 0%, rgba(128, 128, 128, 0) 100%);
}

.splashScreen .splashScreenImg {
    background: transparent url('../img/splashScreen/light.png') no-repeat center center;
    background-size: 100%;
    width: 100%;
    height: 100vh;
    z-index: 4;
    position: absolute;
}
@media (min-width: 400px) {
    .splashScreen .splashScreenImg {
        background-size: 400px;
    }
}

html:not([lang='fa']) .splashScreen .splashScreenImg {
    background-image: url('../img/splashScreen/light-en.png');
}
[data-bs-theme='dark'] .splashScreen .splashScreenImg {
    background-image: url('../img/splashScreen/dark.png');
}
html:not([lang='fa'])[data-bs-theme='dark'] .splashScreen .splashScreenImg {
    background-image: url('../img/splashScreen/dark-en.png');
}

.splashScreen .loading {
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    bottom: 35px;
    float: right;
    width: 100%;
}

.splashScreen .loading svg path:nth-child(1) {
    stroke-dasharray: 2.42777px, 242.77666px;
    stroke-dashoffset: 0;
    -webkit-animation: anim 1.6s linear infinite;
    animation: anim 1.6s linear infinite;
    stroke: #727272;
}
.splashScreen .loading svg path:nth-child(2) {
    stroke: #000;
    opacity: 0.1;
}
[data-bs-theme='dark'] .splashScreen .loading svg path:nth-child(1) {
    stroke: #fff;
}
[data-bs-theme='dark'] .splashScreen .loading svg path:nth-child(2) {
    stroke: #ededed;
    opacity: 0.1;
}
@keyframes anim {
    12.5% {
        stroke-dasharray: 33.98873px, 242.77666px;
        stroke-dashoffset: -26.70543px;
    }
    43.75% {
        stroke-dasharray: 84.97183px, 242.77666px;
        stroke-dashoffset: -84.97183px;
    }
    100% {
        stroke-dasharray: 2.42777px, 242.77666px;
        stroke-dashoffset: -240.34889px;
    }
}

.socialMedia {
    float: left;
    width: 100%;
    margin: 25px 0 15px 0;
}

.socialMedia .item {
    float: left;
    width: 100%;
    background: #f3f3f3;
    border-radius: 25px;
    padding: 10px;
    margin-bottom: 7px;
}

[data-bs-theme='dark'] .socialMedia .item {
    background: #252525;
}

.socialMedia a {
    color: #4c4b4b;
    transition: all 0.2s ease-in-out;
}

[data-bs-theme='dark'] .socialMedia a {
    color: #dcdcdc;
}

.socialMedia a:hover {
    color: #90610f;
}

[data-bs-theme='dark'] .socialMedia a:hover {
    color: #ffa200;
}

.socialMedia .item .icon {
    float: left;
}

.socialMedia .item .icon img {
    float: left;
    width: 25px;
    height: 25px;
    margin: 0 0 0 5px;
    border-radius: 50%;
}

[data-bs-theme='dark'] .socialMedia .item .icon img[alt='github'] {
    filter: contrast(0%);
}

.socialMedia .item .icon i {
    float: left;
    font-size: 25px;
    margin: 0 0 0 5px;
}

.socialMedia .item .host {
    float: left;
    font-size: 16px;
    padding: 4px 0 0 10px;
    opacity: 0.7;
}

.socialMedia .item .name {
    float: right;
    padding: 3px 10px 0 0;
    font-weight: 400;
    font-size: 16px;
}

.socialMedia hr {
    border-color: #dcdcdc;
    margin-top: 0;
}

.socialMedia .starBadge {
    margin-right: 10px;
    width: 70px;
    height: 20px;
}

[dir='ltr'] .socialMedia .starBadge {
    margin-right: 0;
    margin-left: 10px;
}

[data-bs-theme='dark'] .socialMedia hr {
    border-color: #2b2e3b;
}

.dialog {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 2;
    transition: opacity 0.15s ease-in-out;
}

.no-opacity {
    opacity: 0;
}

.dialog .dialogBg {
    position: fixed;
    width: 100%;
    height: 100vh;
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.16);
    backdrop-filter: blur(4px);
    --webkit-backdrop-filter: blur(4px);
    background: rgba(0, 0, 0, 0.25);
    z-index: 2;
    top: 0;
}

[data-bs-theme='dark'] .dialog .dialogBg {
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.16);
    backdrop-filter: blur(3px);
    --webkit-backdrop-filter: blur(3px);
    background-color: rgba(0, 0, 0, 0.25);
}

.dialog .dialogBox {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    background: #fff;
    z-index: 3;
    border-radius: 25px 25px 0 0;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.16);
    padding: 10px 10px 15px 10px;
    left: 50%;
    transform: translateX(-50%);
    max-width: 385px;
}
.dialog .dialogBox .container {
    width: 100%;
}

[data-bs-theme='dark'] .dialog .dialogBox {
    background: #14172d;
}

.dialog .dialogBox .line {
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0 0 5px 0;
}

.dialog .dialogBox .line .miniLine {
    width: 62px;
    height: 5px;
    background: #a1a1a1;
    opacity: 0.37;
    border-radius: 25px;
}

.dialog .dialogBox h3 {
    float: right;
    width: 100%;
    font-weight: 300;
    font-size: 17px;
    margin: 10px 0 20px 0;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[data-bs-theme='dark'] .dialog .dialogBox h3 {
    color: #dcdcdc;
}

.dialog .dialogBox p {
    float: right;
    width: 100%;
    margin: 0;
    font-size: 15px;
    line-height: 26px;
}

.dialog .dialogBox p.withMargin {
    margin-bottom: 20px;
}

.dialog .dialogBox p a {
    color: #ffa200;
}

.dialog .dialogBox label {
    float: left;
    width: 100%;
    font-size: 13px;
    font-weight: 300;
}
.dialog .dialogBox label:not(.firstItem) {
    margin-top: 20px;
}
.dialog .dialogBox label[dir='ltr'] {
    text-align: left;
}

.dialog .dialogBox input,
.dialog .dialogBox textarea {
    float: right;
    width: 100%;
    padding: 15px;
    height: auto;
    direction: ltr;
    font-weight: 400;
    border-width: 2px;
    border-radius: 5px;
}

.dialog .dialogBox textarea {
    min-height: 100px;
    border-radius: 5px;
    height: 150px;
    max-height: 200px;
    resize: vertical;
}

/* .dialog .dialogBox input[type=number] {
    -moz-appearance: textfield;
} */

.dialog .dialogBox input::-webkit-outer-spin-button,
.dialog .dialogBox input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.dialog .dialogBox .input-group {
    float: left;
    width: 100%;
}
.dialog .dialogBox .input-group input {
    float: left;
}
.dialog .dialogBox .input-group input:nth-child(1) {
    border-radius: 5px 0 0 5px;
    width: 30%;
    border-right-width: 1px;
}
.dialog .dialogBox .input-group input:nth-child(2) {
    border-radius: 0;
    width: 55%;
    border-left-width: 1px;
    border-right-width: 1px;
}
.dialog .dialogBox .input-group .input-group-btn {
    float: right;
    width: 15%;
}
.dialog .dialogBox .input-group .input-group-btn button {
    float: right;
    width: 100%;
    border-radius: 0 5px 5px 0;
    margin: 0;
    height: 54px;
    background: #619fd3;
    outline: none;
    color: #fff;
    font-size: 18px;
}
[data-bs-theme='dark'] .dialog .dialogBox .input-group .input-group-btn button {
    background: #316491;
}
.dialog .dialogBox .input-group .input-group-btn button[disabled] {
    background: #cbcbcb;
}
[data-bs-theme='dark'] .dialog .dialogBox .input-group .input-group-btn button[disabled] {
    background: #5a6269;
}
.dialog .dialogBox .input-group .input-group-btn button i {
    float: right;
    width: 100%;
    text-align: center;
    font-size: 18px;
}

[data-bs-theme='dark'] .dialog .dialogBox input,
[data-bs-theme='dark'] .dialog .dialogBox textarea {
    background: #0a0e24;
    border-color: #486097;
    color: #ffffff;
}

[data-bs-theme='dark'] .dialog .dialogBox input:focus,
[data-bs-theme='dark'] .dialog .dialogBox textarea:focus {
    border-color: #5a78bc;
}

.dialog .dialogBox .labels {
    float: left;
    margin: 0;
}

[dir='ltr'] .dialog .dialogBox .labels {
    float: right;
}

.dialog .dialogBox .labels .label {
    float: right;
    border-radius: 25px;
    font-weight: 200;
    padding: 3px 7px;
    margin: 0 5px 0 0;
    cursor: pointer;
    position: relative;
}
.dialog .dialogBox .labels .label.disabled {
    opacity: 0.2;
    cursor: default;
}

[dir='ltr'] .dialog .dialogBox .labels .label {
    float: left;
    margin: 0 0 0 5px;
}

.dialog .dialogBox .labels .label .dropDownInLabel {
    position: absolute;
    top: 25px;
    left: 5px;
    background: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    border-radius: 3px;
    direction: ltr;
    float: left;
    width: 80px;
    height: 120px;
    overflow: hidden;
    overflow-y: auto;
    z-index: 1;
    cursor: default;
}
[dir='ltr'] .dialog .dialogBox .labels .label .dropDownInLabel {
    left: inherit;
    right: 5px;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter {
    float: left;
    width: 162px;
    overflow: hidden;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter[data-list='1'] {
    width: 80px;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter[data-list='3'] {
    width: 240px;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter .split {
    float: left;
    width: 80px;
    height: 120px;
    overflow: hidden;
    overflow-y: auto;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter .split::-webkit-scrollbar-thumb {
    background: rgba(190, 190, 190, 0.9);
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter .split::-webkit-scrollbar {
    background: #fff;
    width: 5px;
}
[data-bs-theme='dark']
    .dialog
    .dialogBox
    .labels
    .label
    .dropDownInLabel.splitter
    .split::-webkit-scrollbar {
    background: #fff;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter .split:first-child {
    border-right: 1px solid #ededed;
}
.dialog .dialogBox .labels .label .dropDownInLabel .item {
    float: right;
    width: 100%;
    padding: 9px 15px;
    font-size: 12px;
    font-weight: 400;
    color: #2c2c2c;
    cursor: pointer;
    border-radius: 3px;
    transition: all 0.2s ease-in-out;
    text-align: left;
}
.dialog .dialogBox .labels .label .dropDownInLabel .item:hover {
    background: #f8f8f8;
    color: #1c1c1c;
}
.dialog .dialogBox .labels .label .dropDownInLabel.splitter .split:nth-child(3) .item {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.dialog .dialogBox .labels .label .dropDownInLabel .item.disabled {
    opacity: 0.3;
    cursor: default;
}
.dialog .dialogBox .labels .label .dropDownInLabel .item.disabled:hover {
    background: #fff;
}
.dialog .dialogBox .labels .label .dropDownInLabel .item small {
    font-size: 12px;
    padding-right: 1px;
    opacity: 0.7;
    font-weight: normal;
}

[data-bs-theme='dark'] .dialog .dialogBox .labels .label.label-warning {
    background-color: #8b5100;
}

[data-bs-theme='dark'] .dialog .dialogBox .labels .label.label-danger {
    background-color: #9b2623;
}

.dialog .dialogBox .labels .label.label-default {
    background-color: #9b9b9b;
}

[data-bs-theme='dark'] .dialog .dialogBox .labels .label.label-default {
    background-color: #7b7b7b;
}

.dialog .dialogBox .labels .label i {
    float: right;
    font-size: 14px;
    margin: -1px -2px 0 3px;
}

[dir='ltr'] .dialog .dialogBox .labels .label i {
    float: left;
    margin: -1px 3px 0 -2px;
}

.dialog .dialogBox .tagList {
    float: left;
    width: 100%;
    margin: 10px 0 0 0;
}
.dialog .dialogBox .tagList .tagItem {
    float: left;
    border: 1px solid #d5d5d5;
    padding: 4px 10px 2px 7px;
    border-radius: 25px;
    line-height: 18px;
    cursor: default;
    margin: 0 7px 7px 0;
}
[data-bs-theme='dark'] .dialog .dialogBox .tagList .tagItem {
    border-color: #337ab7;
}
.dialog .dialogBox .tagList .tagItem i {
    float: left;
    font-size: 16px;
    margin: 0 5px 0 0;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
    color: #adadad;
}
[data-bs-theme='dark'] .dialog .dialogBox .tagList .tagItem i {
    color: #7a7a7a;
}
.dialog .dialogBox .tagList .tagItem i:hover {
    color: #fff;
}
.dialog .dialogBox .tagList .tagItem i.closeIco {
    border-right: 1px solid #d3d3d3;
    padding-right: 5px;
}
[data-bs-theme='dark'] .dialog .dialogBox .tagList .tagItem i.closeIco {
    border-color: #273643;
}
.dialog .dialogBox .tagList .tagItem i.closeIco:hover {
    color: tomato;
}
.dialog .dialogBox .tagList .tagItem span {
    float: left;
    font-size: 13px;
    font-weight: 300;
    text-transform: capitalize;
}
[data-bs-theme='dark'] .dialog .dialogBox .tagList .tagItem span {
    font-weight: 200;
}

.dialog .dialogBox .btn {
    float: left;
    padding: 10px;
    margin: 15px 0 0 0;
    border: 1px solid transparent;
    transition: all 0.2s ease-in-out;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

[dir='ltr'] .dialog .dialogBox .btn {
    float: right;
}

.dialog .dialogBox .btn.btn-save {
    background: #8d8c8c;
    color: #fff;
    font-weight: 300;
}

[data-bs-theme='dark'] .dialog .dialogBox .btn.btn-save {
    background: #050920;
}

.dialog .dialogBox .btn.btn-save:hover {
    background: #7f7f7f;
}

[data-bs-theme='dark'] .dialog .dialogBox .btn.btn-save:hover {
    background: #010315;
}

.dialog .dialogBox .btn.btn-cancel {
    border-color: #acacac;
    margin-right: 10px;
    font-weight: 300;
}

[dir='ltr'] .dialog .dialogBox .btn.btn-cancel {
    margin-right: 0;
    margin-left: 10px;
}

[data-bs-theme='dark'] .dialog .dialogBox .btn.btn-cancel {
    border-color: #303666;
    font-weight: 200;
}

.dialog .dialogBox .btn.btn-cancel:hover {
    border-color: #979797;
}

[data-bs-theme='dark'] .dialog .dialogBox .btn.btn-cancel:hover {
    border-color: #464d8b;
    color: #dadada;
}

.dialog .dialogBox .btn.btn-update {
    border-color: #acacac;
    margin-left: 10px;
    font-weight: 300;
    float: right;
}

[dir='ltr'] .dialog .dialogBox .btn.btn-update {
    margin-left: 0;
    margin-right: 10px;
    float: left;
}

[data-bs-theme='dark'] .dialog .dialogBox .btn.btn-update {
    border-color: #303666;
}

.dialog .dialogBox .btn.btn-update:hover {
    border-color: #979797;
}

[data-bs-theme='dark'] .dialog .dialogBox .btn.btn-update:hover {
    border-color: #464d8b;
    color: #dadada;
}

.dialog .dialogBox .btn.btn-update i {
    float: right;
    margin: 0 1px 0 1px;
    transition: all 0.2s ease-in-out;
    opacity: 0.5;
    user-select: none;
    cursor: pointer;
    line-height: 20px;
}
[dir='ltr'] .dialog .dialogBox .btn.btn-update i {
    float: left;
}
.dialog .dialogBox .btn.btn-update i:hover {
    opacity: 0.8;
}

.customToast {
    float: right;
    width: 100%;
}

.customToast p {
    float: right;
    width: 100%;
    font-size: 14px;
    line-height: 21px;
    font-weight: 200;
    margin: 0 0 10px 0;
}

.customToast button {
    float: left;
    border: none;
    border-radius: 25px;
    background: transparent;
    font-size: 14px;
    font-weight: 200;
    transition: all 0.2s ease-in-out;
    opacity: 0.7;
    padding: 4px 7px;
}

.customToast button:hover {
    opacity: 1;
}

.drawer {
    padding: 15px 0;
    margin: 0;
    max-width: 310px;
    user-select: none;
}

[data-bs-theme='dark'] .drawer {
    background: #0a0e24 !important;
}

.drawerOverlay {
    backdrop-filter: blur(4px);
    --webkit-backdrop-filter: blur(4px);
    background-color: rgba(0, 0, 0, 0.25) !important;
}

@media (min-width: 1050px) {
    .navMenu,
    .tabs.inHome,
    .drawerOverlay {
        display: none !important;
    }
    .EZDrawer .EZDrawer__container {
        background: transparent !important;
        box-shadow: none !important;
        border-left: 1px solid #f3f3f3;
    }
    [data-bs-theme='dark'] .EZDrawer .EZDrawer__container {
        border-left: 1px solid #2b2e3b;
    }
    [dir='ltr'] .EZDrawer .EZDrawer__container {
        border-left: none;
        border-right: 1px solid #f3f3f3;
    }
    [dir='ltr'][data-bs-theme='dark'] .EZDrawer .EZDrawer__container {
        border-left: none;
        border-right: 1px solid #2b2e3b;
    }
}

[data-bs-theme='dark'] .drawerOverlay {
    backdrop-filter: blur(3px);
    --webkit-backdrop-filter: blur(3px);
}

.drawer .list {
    float: right;
    width: 100%;
}

.drawer .list .appName {
    float: right;
    width: 100%;
    margin: 0 0 15px 0;
    padding: 0 20px 15px 0;
}

[dir='ltr'] .drawer .list .appName {
    float: left;
    padding: 0 0 15px 20px;
}

[dir='ltr'] .drawer .list .appName {
    float: left;
}

.drawer .list .appName img {
    float: right;
    width: 80px;
    height: 80px;
    object-fit: cover;
    margin: 0 -5px 8px 0;
}

[dir='ltr'] .drawer .list .appName img {
    float: left;
    margin: 0 0 8px -5px;
}

.drawer .list .appName h3 {
    float: right;
    width: 100%;
    margin: 0;
    padding: 0;
    font-size: 22px;
    font-weight: 400;
    color: #ffa200;
}

[dir='ltr'] .drawer .list .appName h3 {
    float: left;
    text-align: left;
}

.drawer .list .appName h3 small {
    font-weight: 200;
    font-size: 80%;
}

[data-bs-theme='dark'] .drawer .list .appName h3 small {
    color: #b0aeae;
}

.drawer .list ul {
    float: right;
    width: 100%;
    margin: 0;
    padding: 0;
}

.drawer .list ul li {
    float: right;
    width: 100%;
    list-style: none;
    transition: all 0.2s ease-in-out;
}

[dir='ltr'] .drawer .list ul li {
    float: left;
}

.drawer .list ul li:hover {
    background: #ededed;
}

[data-bs-theme='dark'] .drawer .list ul li:hover {
    background: #161c3e;
}

.drawer .list ul li.divider {
    border-bottom: 1px solid #f3f3f3;
    padding: 0;
    margin: 10px 0;
}

[data-bs-theme='dark'] .drawer .list ul li.divider {
    border-color: #2b2e3b;
}

.drawer .list ul li.divider:hover {
    background: transparent;
}

.drawer .list ul li a {
    float: right;
    width: 100%;
    padding: 10px 20px;
    cursor: pointer;
}

[dir='ltr'] .drawer .list ul li a {
    float: left;
}

.drawer .list ul li i {
    float: right;
    color: #b0b0b0;
    font-size: 22px !important;
    padding-left: 20px;
    transition: all 0.2s ease-in-out;
}

[dir='ltr'] .drawer .list ul li i {
    float: left;
    padding-left: 0;
    padding-right: 20px;
    margin-top: -2px;
}

.drawer .list ul li:hover i {
    color: #8d8d8d;
}

[data-bs-theme='dark'] .drawer .list ul li:hover i {
    color: #d0d0d0;
}

.drawer .list ul li span {
    float: right;
    color: #4d4d4d;
    padding: 1px 0 0 0;
    font-weight: 300;
    font-size: 16px;
}

[dir='ltr'] .drawer .list ul li span {
    float: left;
}

[data-bs-theme='dark'] .drawer .list ul li span {
    color: #e1e1e1;
    font-weight: 200;
}

.drawer .list ul li .label {
    float: left;
    font-weight: 200;
    border-radius: 25px;
    padding: 3px 5px;
    font-size: 12px;
    margin: 2px 0 0 0;
    background: #ffa200;
}

[dir='ltr'] .drawer .list ul li .label {
    float: right;
}

.drawer .loader {
    float: left;
    position: relative;
    width: 7px;
    height: 7px;
    margin-top: 8px;
    margin-inline-end: 9px;
    border-radius: 4px;
    background-color: #ffa200;
    color: #ffa200;
    animation: dotFlashing 1s infinite linear alternate;
    animation-delay: 0.5s;
}

[dir='ltr'] .drawer .loader {
    float: right;
}

.drawer .loader::before,
.drawer .loader::after {
    content: '';
    display: inline-block;
    position: absolute;
    top: 0;
}

.drawer .loader::before {
    left: -10px;
    width: 7px;
    height: 7px;
    border-radius: 4px;
    background-color: #ffa200;
    color: #ffa200;
    animation: dotFlashing 1s infinite alternate;
    animation-delay: 0s;
}

.drawer .loader::after {
    left: 10px;
    width: 7px;
    height: 7px;
    border-radius: 4px;
    background-color: #ffa200;
    color: #ffa200;
    animation: dotFlashing 1s infinite alternate;
    animation-delay: 1s;
}

@keyframes dotFlashing {
    0% {
        background-color: #ffa200;
    }
    50%,
    100% {
        background-color: #ffa20033;
    }
}

.drawer .appVersion {
    float: right;
    width: 100%;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    text-align: center;
    padding: 20px;
    font-size: 12px;
    color: #c0c0c0;
}

.drawer .appVersion b {
    font-size: 13px;
}

.tabs {
    position: fixed;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 100%;
    max-width: 385px;
    margin: 0;
    z-index: 2;
    padding: 1px 0;
    background: rgba(255, 255, 255, 0.5);
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
    user-select: none;
}

[data-bs-theme='dark'] .tabs {
    background: rgba(10, 14, 36, 0.8);
}

.tabs.inSettings {
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.1);
}
[data-bs-theme='dark'] .tabs.inSettings {
    box-shadow: 0 0 30px rgba(255, 255, 255, 0.02);
}

@media (min-width: 400px) {
    .tabs.inSettings,
    [data-bs-theme='dark'] .tabs.inSettings {
        box-shadow: none;
    }
}

.tabs ul {
    float: right;
    width: 100%;
    margin: 0;
    padding: 0;
    transition: all 0.2s ease-in-out;
    max-height: 61px;
}

.tabs ul li {
    float: right;
    width: 20%;
    list-style: none;
    margin: 0;
}
.tabs.withSingbox ul li {
    width: 16.66%;
}

[dir='ltr'] .tabs ul li {
    float: left;
}

.tabs ul li a {
    float: right;
    width: 100%;
    padding: 7px 10px;
    text-decoration: none;
    transition: all 0.2s ease-in-out;
    font-size: 13px;
    font-weight: 300;
    color: #898989;
    text-align: center;
    line-height: 17px;
}
.tabs.withSingbox ul li a {
    padding: 15px 10px;
}

[data-bs-theme='dark'] .tabs ul li a {
    color: #909090;
}

.tabs ul li.active a {
    color: #af8b4c;
}

[data-bs-theme='dark'] .tabs ul li.active a {
    color: #749af8;
}

.tabs ul li a span {
    float: right;
    width: 100%;
    text-align: center;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    transition: all 0.2s ease-in-out;
}
.tabs.withSingbox ul li a span {
    opacity: 0;
    height: 0;
}

.tabs ul li a i {
    font-size: 22px;
    padding: 2px 10px;
    border-radius: 25px;
    color: #c0c0c0;
    transition: all 0.2s ease-in-out;
}

[data-bs-theme='dark'] .tabs ul li a i {
    color: #5f5f5f;
}

.tabs ul li.active a i {
    background: #b9975b;
    color: #fff;
}

[data-bs-theme='dark'] .tabs ul li.active a i {
    background: #273286;
}

.contextMenu {
    background: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    border-radius: 3px;
    width: 75px;
    direction: ltr;
    z-index: 3;
}
.contextMenu .menuItem {
    float: right;
    width: 100%;
    padding: 3px 15px;
    font-size: 12px;
    color: #2c2c2c;
    cursor: pointer;
    border-radius: 3px;
    transition: all 0.2s ease-in-out;
}
.contextMenu .menuItem:hover {
    background: #f8f8f8;
    color: #1c1c1c;
}

.speedTest {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    transition: all 0.5s ease;
}

.speedTest button.startButton {
    position: relative;
    width: 120px;
    height: 120px;
    margin-top: 100px;
    border: none;
    border-radius: 50%;
    color: #757575;
    font-size: 55px;
    font-weight: bold;
    border: 1px solid #ffa200;
    transition: all 0.5s ease;
    animation-name: startButton;
    animation-iteration-count: infinite;
    animation-duration: 3s;
}
[data-bs-theme='dark'] .speedTest button.startButton {
    color: #fff;
}
@keyframes startButton {
    0% {
        box-shadow: 0 0 0 rgba(245, 156, 10, 0.4);
    }
    50% {
        box-shadow: -1px 0px 0 12px rgba(245, 156, 10, 0.15);
    }
    100% {
        box-shadow: 0 0 0 rgba(245, 156, 10, 0.4);
    }
}
.speedTest button.startButton:hover {
    color: #535353;
}
[data-bs-theme='dark'] .speedTest button.startButton:hover {
    color: #ffa200;
}

.speedTest button.startButton[data-type='disabled'] {
    background: #fdfdfd;
    box-shadow: 0 0 5px 5px rgba(117, 116, 116, 0.36);
    animation: inherit;
    box-shadow: none;
    color: #757575 !important;
}
[data-bs-theme='dark'] .speedTest button.startButton[data-type='disabled'] {
    background: #0a0e24;
    color: #fff !important;
}

.speedTest button.startButton[data-type='enabled'] {
    background: transparent;
    cursor: pointer;
}

.speedTest button.startButton[data-type='finished'] {
    background: #e1e1e1;
    box-shadow: 0 0 5px 5px rgba(68, 73, 69, 0.39);
    cursor: pointer;
}
[data-bs-theme='dark'] .speedTest button.startButton[data-type='finished'] {
    background: #3d3d3d;
}

.testRunning button.startButton::before {
    content: '';
    position: absolute;
    top: -10px;
    left: -10px;
    right: -10px;
    bottom: -10px;
    border-radius: 50%;
    border: 5px solid transparent;
    border-top: 5px solid #ffa200;
    border-right: 5px solid #ffa200;
    animation: spinner 0.8s linear infinite;
}

.speedTest.testRunning button.startButton,
.speedTest.testDone button.startButton {
    width: 80px;
    height: 80px;
    margin-top: 80px;
    transform: translateY(-80%);
    box-shadow: none;
    animation-name: inherit;
    border-color: #e3e3e3;
    font-size: 33px;
}
.speedTest.testDone button.startButton {
    border-color: #bfbfbf;
}
[data-bs-theme='dark'] .speedTest.testRunning button.startButton,
[data-bs-theme='dark'] .speedTest.testDone button.startButton {
    border-color: #757575;
}

.speedTest span.statusMessage {
    font-size: 15px;
    margin-top: 70px;
    font-weight: 200;
    text-align: center;
    color: #303030;
}
[data-bs-theme='dark'] .speedTest span.statusMessage {
    color: #dcdcdc;
}

.speedTest .results {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 20px;
}

.speedTest .results .resultRow {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    width: 100%;
}

.speedTest .results .resultCard {
    width: 150px;
    margin: 7px;
    padding: 12px;
    text-align: center;
    background: #f9f9f9;
    border-radius: 8px;
    border: 1px solid rgba(0, 0, 0, 0.1);
}

[data-bs-theme='dark'] .speedTest .results .resultCard {
    background: #1b2337;
    border: 1px solid rgba(234, 229, 229, 0.15);
}

.speedTest .results .resultCard p {
    color: #666;
    font-size: 13px;
}

[data-bs-theme='dark'] .speedTest .results .resultCard p {
    color: #afaeae;
}

.speedTest .results .resultCard h2 {
    color: #333;
    font-size: 16px;
    direction: ltr;
    margin-bottom: 5px;
}
[data-bs-theme='dark'] .speedTest .results .resultCard h2 {
    color: #e3e3e3;
}

.speedTest .results .resultCard h2 small {
    font-size: 14px;
}

.progressBar {
    width: 100%;
    height: 3px;
    position: fixed;
    top: 0;
    left: 0;
    overflow: hidden;
    background: linear-gradient(
        135deg,
        #c4c4c4 25%,
        transparent 25%,
        transparent 50%,
        #c4c4c4 50%,
        #c4c4c4 75%,
        transparent 75%,
        transparent
    );
    background-size: 30px 30px;
    animation: moveStripes 0.5s linear infinite;
    will-change: background-position;
}
[data-bs-theme='dark'] .progressBar {
    background: linear-gradient(
        135deg,
        #5e5e5e 25%,
        transparent 25%,
        transparent 50%,
        #5e5e5e 50%,
        #5e5e5e 75%,
        transparent 75%,
        transparent
    );
    background-size: 30px 30px;
}
@keyframes moveStripes {
    0% {
        background-position: 0 0;
    }
    100% {
        background-position: 30px 0;
    }
}
.progressBar div {
    height: 3px;
    transition: width 0.3s ease-in-out;
    float: left;
}
.progressBar.green div {
    background: #4caf50;
}
.progressBar.red div {
    background: tomato;
}


================================================
FILE: assets/entitlements.mac.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
  </dict>
</plist>


================================================
FILE: assets/json/1713988096625.json
================================================
{"nm":"Ella Shimmer","ddd":0,"h":300,"w":300,"meta":{"g":"LottieFiles AE 3.0.2"},"layers":[{"ty":3,"nm":"Null 1","sr":1,"st":0,"op":94,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[150,150,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":0,"ix":11}},"ef":[],"ind":1},{"ty":0,"nm":"Comp 1","sr":1,"st":62,"op":152,"ip":62,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[150,175,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":200,"h":100,"refId":"comp_0","ind":2},{"ty":0,"nm":"Comp 1","sr":1,"st":30,"op":120,"ip":30,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.069,"y":0.995},"s":[100,100,100],"t":62},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":76},{"s":[80,80,100],"t":94}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.182,"y":1},"s":[0,25,0],"t":62,"ti":[0,8.583,0],"to":[0,-8.583,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.182,"y":0.182},"s":[0,-26.5,0],"t":76,"ti":[0,0,0],"to":[0,0,0]},{"s":[0,-26.5,0],"t":94}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":62},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[60],"t":76},{"s":[60],"t":94}],"ix":11}},"ef":[],"w":200,"h":100,"refId":"comp_0","ind":3,"parent":1},{"ty":0,"nm":"Comp 1","sr":1,"st":-2,"op":88,"ip":-2,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.069,"y":0.995},"s":[100,100,100],"t":30},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":44},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":62},{"s":[50,50,100],"t":76}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.182,"y":1},"s":[150,175,0],"t":30,"ti":[0,8.583,0],"to":[0,-8.583,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.182,"y":0.182},"s":[150,123.5,0],"t":44,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.167,"y":0},"i":{"x":0.182,"y":1},"s":[150,123.5,0],"t":62,"ti":[0,6.167,0],"to":[0,-6.167,0]},{"s":[150,86.5,0],"t":76}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":30},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[60],"t":44},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[60],"t":62},{"s":[0],"t":76}],"ix":11}},"ef":[],"w":200,"h":100,"refId":"comp_0","ind":4},{"ty":0,"nm":"Comp 1","sr":1,"st":-33,"op":57,"ip":-33,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.069,"y":0.995},"s":[100,100,100],"t":-1},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":13},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":31},{"s":[50,50,100],"t":45}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.182,"y":1},"s":[0,25,0],"t":-1,"ti":[0,8.583,0],"to":[0,-8.583,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.182,"y":0.182},"s":[0,-26.5,0],"t":13,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.167,"y":0},"i":{"x":0.182,"y":1},"s":[0,-26.5,0],"t":31,"ti":[0,6.167,0],"to":[0,-6.167,0]},{"s":[0,-63.5,0],"t":45}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":-1},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[60],"t":13},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[60],"t":31},{"s":[0],"t":45}],"ix":11}},"ef":[],"w":200,"h":100,"refId":"comp_0","ind":5,"parent":1},{"ty":0,"nm":"Comp 1","sr":1,"st":-76,"op":14,"ip":-76,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.069,"y":0.995},"s":[100,100,100],"t":-35},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":-21},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[80,80,100],"t":-1},{"s":[50,50,100],"t":13}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.182,"y":1},"s":[150,175,0],"t":-35,"ti":[0,8.583,0],"to":[0,-8.583,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.182,"y":0.182},"s":[150,123.5,0],"t":-21,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.167,"y":0},"i":{"x":0.182,"y":1},"s":[150,123.5,0],"t":-1,"ti":[0,6.167,0],"to":[0,-6.167,0]},{"s":[150,86.5,0],"t":13}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":-35},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[60],"t":-21},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[60],"t":-1},{"s":[0],"t":13}],"ix":11}},"ef":[],"w":200,"h":100,"refId":"comp_0","ind":6}],"v":"4.8.0","fr":30,"op":94,"ip":30,"assets":[{"nm":"","id":"comp_0","layers":[{"ty":4,"nm":"Shape Layer 3","sr":1,"st":0,"op":118,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-30,-6.544,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.651,"y":0.998},"s":[0,75.476,100],"t":9},{"o":{"x":0.379,"y":0.013},"i":{"x":0.524,"y":0.97},"s":[110,75.476,100],"t":21},{"s":[100,75.476,100],"t":29}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[81,59.26,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":2,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"s":{"a":0,"k":[85.26,14.271],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8784,0.9059,0.9373],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[12.63,-8.364],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":166,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-30,-6.544,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.651,"y":0.997},"s":[0,75.476,100],"t":3},{"o":{"x":0.379,"y":0.027},"i":{"x":0.524,"y":0.94},"s":[90,75.476,100],"t":15},{"s":[85,75.476,100],"t":23}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[81,41.26,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":2,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"s":{"a":0,"k":[85.26,14.271],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8784,0.9059,0.9373],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[12.63,-8.364],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":166,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-66.789,-32.789,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.044,"y":0.991},"s":[0,0,100],"t":0},{"s":[93,93,100],"t":12}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[48.961,49.211,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":2,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":4,"ix":4},"s":{"a":0,"k":[38.422,38.422],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.8941,0],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-66.789,-32.789],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3}]}]}

================================================
FILE: assets/proto/oblivion.proto
================================================
syntax = "proto3";

package oblivionHelper;

service OblivionService {
  rpc Start (StartRequest) returns (StartResponse);
  rpc Stop (StopRequest) returns (StopResponse);
  rpc StreamStatus (StatusRequest) returns (stream StatusResponse);
  rpc Exit (ExitRequest) returns (ExitResponse);
}

message StartRequest {}
message StartResponse {
  string message = 1;
}
message StopRequest {}
message StopResponse {
  string message = 1;
}
message StatusRequest {}
message StatusResponse {
  string status = 1;
}
message ExitRequest {}
message ExitResponse {}

================================================
FILE: package.json
================================================
{
    "name": "oblivion-desktop",
    "description": "unofficial desktop version of oblivion",
    "shortName": "oblivion",
    "author": "ircfspace+kiomarzsss <ircfspace@gmail.com> (https://ircf.space/)",
    "homepage": "https://github.com/bepass-org/oblivion-desktop#readme",
    "repository": {
        "type": "git",
        "url": "git+https://github.com/bepass-org/oblivion-desktop.git"
    },
    "bugs": {
        "url": "https://github.com/bepass-org/oblivion-desktop/issues",
        "email": "ircfspace@gmail.com"
    },
    "version": "3.11.0",
    "license": "Restrictive",
    "main": "./.erb/dll/main.bundle.dev.js",
    "scripts": {
        "eval": "eval process.",
        "build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
        "build:dll": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
        "build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
        "build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
        "prepare": "ts-node script/dlBins.ts && husky",
        "postinstall": "ts-node ./script/postinstall.ts",
        "postinstall:darwin-linux": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && npm run build:dll",
        "postinstall:windows": "ts-node script/makeRegeditVBSAvailable.ts && ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && npm run build:dll",
        "lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
        "package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never && npm run build:dll",
        "package:linux": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder --linux --dir --publish never && npm run build:dll",
        "package:mac": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder  --mac --dir --publish never && npm run build:dll",
        "package:windows": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder --windows --dir  --publish never && npm run build:dll",
        "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
        "prestart": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.dev.ts",
        "start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run prestart && npm run start:renderer",
        "dev": "npm start",
        "start:main": "concurrently -k \"cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --watch --config ./.erb/configs/webpack.config.main.dev.ts\" \"electronmon .\"",
        "start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
        "start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
        "test": "jest",
        "format": "prettier --write . --config .prettierrc --ignore-path .prettierignore --cache",
        "format:check": "prettier . --check --cache --config .prettierrc --ignore-path .prettierignore",
        "tsc": "tsc --noEmit --incremental",
        "changeVersion": "ts-node ./script/changeVersion"
    },
    "browserslist": [],
    "prettier": {
        "singleQuote": true,
        "overrides": [
            {
                "files": [
                    ".prettierrc",
                    ".eslintrc"
                ],
                "options": {
                    "parser": "json"
                }
            }
        ]
    },
    "jest": {
        "moduleDirectories": [
            "node_modules",
            "release/app/node_modules",
            "src"
        ],
        "moduleFileExtensions": [
            "js",
            "jsx",
            "ts",
            "tsx",
            "json"
        ],
        "moduleNameMapper": {
            "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js",
            "\\.(css|less|sass|scss)$": "identity-obj-proxy"
        },
        "setupFiles": [
            "./.erb/scripts/check-build-exists.ts"
        ],
        "testEnvironment": "jsdom",
        "testEnvironmentOptions": {
            "url": "http://localhost/"
        },
        "testPathIgnorePatterns": [
            "release/app/dist",
            ".erb/dll"
        ],
        "transform": {
            "\\.(ts|tsx|js|jsx)$": "ts-jest"
        }
    },
    "dependencies": {
        "@cloudflare/speedtest": "^1.7.0",
        "@grpc/grpc-js": "^1.14.2",
        "@grpc/proto-loader": "^0.8.0",
        "axios": "^1.13.2",
        "classnames": "^2.5.1",
        "electron-log": "^5.4.3",
        "electron-settings": "^4.0.4",
        "lodash": "^4.17.21",
        "node-aplay": "^1.0.3",
        "react": "^19.2.1",
        "react-dom": "^19.2.1",
        "react-hot-toast": "^2.6.0",
        "react-modern-drawer": "^1.4.0",
        "react-router": "^7.10.1",
        "regedit": "^5.1.4",
        "serve-handler": "^6.1.5",
        "sound-play": "^1.1.0",
        "sudo-prompt": "^9.2.1",
        "systeminformation": "^5.27.13",
        "tree-kill": "^1.2.2",
        "zustand": "^5.0.9"
    },
    "devDependencies": {
        "@electron/notarize": "^2.5.0",
        "@electron/rebuild": "^3.7.2",
        "@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
        "@svgr/webpack": "^8.1.0",
        "@testing-library/jest-dom": "^6.9.1",
        "@testing-library/react": "^16.3.0",
        "@types/decompress": "^4.2.7",
        "@types/detect-port": "^1.3.5",
        "@types/electron": "^1.4.38",
        "@types/jest": "^30.0.0",
        "@types/lodash": "^4.17.21",
        "@types/node": "^24.10.2",
        "@types/react": "^19.2.7",
        "@types/react-dom": "^19.2.3",
        "@types/serve-handler": "^6.1.4",
        "@types/sound-play": "^1.1.3",
        "@types/webpack-bundle-analyzer": "^4.7.0",
        "@typescript-eslint/eslint-plugin": "^6.7.0",
        "@typescript-eslint/parser": "^6.7.0",
        "chalk": "^4.1.2",
        "concurrently": "^9.2.1",
        "cross-env": "^10.1.0",
        "css-loader": "^7.1.2",
        "css-minimizer-webpack-plugin": "^7.0.3",
        "decompress": "^4.2.1",
        "detect-port": "^2.1.0",
        "electron": "^39.2.6",
        "electron-builder": "^26.0.12",
        "electronmon": "^2.0.4",
        "eslint": "^8.49.0",
        "eslint-config-airbnb-base": "^15.0.0",
        "eslint-config-erb": "^4.1.0-0",
        "eslint-import-resolver-typescript": "^3.10.1",
        "eslint-import-resolver-webpack": "^0.13.10",
        "eslint-plugin-compat": "^4.2.0",
        "eslint-plugin-import": "^2.32.0",
        "eslint-plugin-jest": "^27.4.0",
        "eslint-plugin-jsx-a11y": "^6.10.2",
        "eslint-plugin-promise": "^6.1.1",
        "eslint-plugin-react": "^7.37.5",
        "eslint-plugin-react-hooks": "^5.2.0",
        "file-loader": "^6.2.0",
        "html-webpack-plugin": "^5.6.5",
        "husky": "^9.1.7",
        "identity-obj-proxy": "^3.0.0",
        "jest": "^30.2.0",
        "mini-css-extract-plugin": "^2.9.4",
        "prettier": "^3.7.4",
        "react-refresh": "^0.18.0",
        "rimraf": "^6.1.2",
        "sass": "^1.95.1",
        "sass-loader": "^16.0.6",
        "style-loader": "^4.0.0",
        "terser-webpack-plugin": "^5.3.15",
        "ts-jest": "^29.4.6",
        "ts-loader": "^9.5.4",
        "ts-node": "^10.9.2",
        "tsconfig-paths-webpack-plugin": "^4.2.0",
        "typescript": "^5.9.3",
        "webpack": "^5.103.0",
        "webpack-bundle-analyzer": "^4.10.2",
        "webpack-cli": "^6.0.1",
        "webpack-dev-server": "^5.2.2",
        "webpack-merge": "^6.0.1"
    },
    "build": {
        "productName": "oblivion-desktop",
        "executableName": "oblivion-desktop",
        "appId": "org.desktop.oblivion",
        "artifactName": "oblivion-desktop-${os}-${arch}.${ext}",
        "asar": true,
        "asarUnpack": "**\\*.{node,dll}",
        "beforePack": "./script/beforePackHook.js",
        "files": [
            "dist",
            "node_modules",
            "package.json",
            "assets/bin/**"
        ],
        "afterSign": ".erb/scripts/notarize.js",
        "protocols": [
            {
                "name": "Oblivion Protocol",
                "schemes": [
                    "oblivion"
                ]
            }
        ],
        "mac": {
            "target": [
                {
                    "target": "dmg",
                    "arch": [
                        "arm64",
                        "x64"
                    ]
                },
                {
                    "target": "zip",
                    "arch": [
                        "arm64",
                        "x64"
                    ]
                }
            ],
            "identity": null
        },
        "dmg": {
            "writeUpdateInfo": true
        },
        "win": {
            "target": [
                {
                    "target": "nsis",
                    "arch": [
                        "x64",
                        "arm64",
                        "ia32"
                    ]
                },
                {
                    "target": "zip",
                    "arch": [
                        "x64",
                        "arm64",
                        "ia32"
                    ]
                }
            ]
        },
        "nsis": {
            "oneClick": false,
            "allowToChangeInstallationDirectory": true,
            "differentialPackage": false,
            "perMachine": true,
            "createDesktopShortcut": true,
            "createStartMenuShortcut": true,
            "runAfterFinish": true,
            "shortcutName": "Oblivion Desktop"
        },
        "linux": {
            "target": [
                {
                    "target": "AppImage",
                    "arch": [
                        "arm64",
                        "x64"
                    ]
                },
                {
                    "target": "deb",
                    "arch": [
                        "arm64",
                        "x64"
                    ]
                },
                {
                    "target": "rpm",
                    "arch": [
                        "arm64",
                        "x64"
                    ]
                },
                {
                    "target": "tar.xz",
                    "arch": [
                        "arm64",
                        "x64"
                    ]
                }
            ],
            "description": "Unofficial Warp Client",
            "category": "Network",
            "icon": "assets"
        },
        "directories": {
            "app": "release/app",
            "buildResources": "assets",
            "output": "release/build"
        },
        "extraResources": [
            "./assets/**"
        ],
        "publish": {
            "provider": "github",
            "owner": "bepass-org",
            "repo": "oblivion-desktop",
            "publishAutoUpdate": true,
            "private": false,
            "releaseType": "draft"
        }
    },
    "engines": {
        "node": ">=20.x",
        "npm": ">=10.x"
    },
    "electronmon": {
        "patterns": [
            "!**/**",
            "src/main/**",
            ".erb/dll/**"
        ],
        "logLevel": "quiet"
    }
}


================================================
FILE: release/app/package.json
================================================
{
    "name": "oblivion-desktop",
    "description": "unofficial desktop version of oblivion",
    "version": "3.11.0",
    "homepage": "https://github.com/bepass-org/oblivion-desktop#readme",
    "license": "Restrictive",
    "author": "ircfspace+kiomarzsss <ircfspace@gmail.com> (https://ircf.space/)",
    "bugs": {
        "url": "https://github.com/bepass-org/oblivion-desktop/issues",
        "email": "ircfspace@gmail.com"
    },
    "main": "./dist/main/main.js",
    "scripts": {
        "rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js",
        "postinstall": "npm run rebuild && npm run link-modules",
        "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts"
    },
    "dependencies": {}
}


================================================
FILE: script/beforePackHook.js
================================================
const util = require('util');
const exec = util.promisify(require('child_process').exec);

exports.default = async function (context) {
    const archDict = {
        0: 'ia32',
        1: 'x64',
        3: 'arm64'
    };
    // TODO don't force download when packaging on the local platform
    const { stdout, stderr } = await exec(
        `npm exec ts-node script/dlBins.ts force ${context.electronPlatformName} ${archDict[context.arch]}`
    );

    if (stderr) {
        console.error(stderr);
    }
    console.log(stdout);
};


================================================
FILE: script/changeVersion.ts
================================================
import fs from 'fs';
import path from 'path';
import { exec } from 'child_process';

let v = process.argv[2];
if (v.charAt(0) === 'v') {
    v = v.substring(1);
}

function changeJson(filePath: string, key: string, value: string, callback: Function) {
    fs.readFile(filePath, 'utf8', (error, data) => {
        if (error) {
            console.error(error);
            return;
        }
        const json = JSON.parse(data);
        json[key] = value;
        fs.writeFile(filePath, JSON.stringify(json), 'utf8', (error2) => {
            if (error2) {
                console.error(error2);
                return;
            }
            callback();
        });
    });
}

changeJson(path.resolve(__dirname, '../package.json'), 'version', v, () => {
    console.log('edited package.json');

    changeJson(path.resolve(__dirname, '../release/app/package.json'), 'version', v, () => {
        console.log('edited release/app/package.json');
        console.log('npm run format...');
        exec('npm run format', (err) => {
            if (err) {
                console.error(err);
                return;
            }

            console.log('git add package.json release/app/package.json');
            exec('git add package.json release/app/package.json', (err2) => {
                if (err2) {
                    console.error(err2);
                    return;
                }

                console.log(`git commit -m "🔖 ${v}"`);
                exec(`git commit -m "🔖 ${v}"`, (err3, stdout2) => {
                    if (err3) {
                        console.error(err3);
                        return;
                    }
                    console.log(stdout2);

                    console.log('git tag v' + v);
                    exec('git tag v' + v, (err4) => {
                        if (err4) {
                            console.error(err4);
                            return;
                        }

                        console.log(`git push`);
                        exec(`git push`, (err5, stdout3) => {
                            if (err5) {
                                console.error(err5);
                                return;
                            }
                            console.log(stdout3);

                            console.log('git push --tags');
                            exec('git push --tags', (err6, stdout4) => {
                                if (err6) {
                                    console.error(err6);
                                    return;
                                }
                                console.log(stdout4);
                            });
                        });
                    });
                });
            });
        });
    });
});


================================================
FILE: script/dlBins.ts
================================================
import fs from 'fs';
import axios from 'axios';
import decompress from 'decompress';
import { doesDirectoryExist, doesFileExist } from '../src/main/lib/utils';
import {
    wpVersion,
    helperVersion,
    netStatsVersion,
    proxyResetVersion,
    mpVersion
} from '../src/main/config';

const forceDownload = process.argv[2] === 'force';
const platform = process.argv[3] || process.platform;
const arch = process.argv[4] || process.arch;

console.log('➡️ platform:', platform);
console.log('➡️ arch:', arch);

async function downloadFile(uri: string, destPath: string) {
    try {
        const response = await axios.get(uri, {
            responseType: 'arraybuffer',
            onDownloadProgress: (progressEvent) => {
                const percentCompleted = Math.round(
                    (progressEvent.loaded * 100) / (progressEvent.total || 1)
                );
                try {
                    process?.stdout?.clearLine(0);
                    process?.stdout?.cursorTo(0);
                    process?.stdout?.write(`Downloading ${uri}: ${percentCompleted}%`);
                } catch (error) {
                    if (
                        !String(error).includes(
                            'TypeError: process?.stdout?.clearLine is not a function'
                        )
                    ) {
                        console.error(error);
                    }
                }
            }
        });

        const buffer = Buffer.from(new Uint8Array(response.data));
        fs.writeFileSync(destPath, buffer);
        console.log();
    } catch (error: any) {
        console.error(`Failed to download ${uri}:`, error.message);
    }
}

async function dlUnzipMove(url: string, binPath: string, zipFileName: string) {
    const isBinDirExist = await doesDirectoryExist(binPath);
    if (!isBinDirExist) {
        fs.mkdirSync(binPath, { recursive: true });
    }

    const zipFilePath = `./${zipFileName}`;

    const isZipFileExist = await doesFileExist(zipFilePath);
    if (!isZipFileExist || forceDownload) {
        console.log(`Downloading ${zipFileName} binary based on your platform and architecture...`);
        await downloadFile(url, zipFilePath);
    } else {
        console.log(`➡️ Skipping Download as ${zipFilePath} already exists.`);
    }

    try {
        await decompress(zipFilePath, binPath, { strip: 1 });
        console.log(`✅ ${zipFileName} binary is ready to use.`);
    } catch (error) {
        console.error(error);
    }
}

const warpPlusUrlBase = `https://github.com/bepass-org/warp-plus/releases/download/v${wpVersion}/warp-plus_`;
const helperUrlBase = `https://github.com/Dr-Bad/oblivion-helper/releases/download/v${helperVersion}/oblivion-helper-`;
const netStatsUrlBase = `https://github.com/ShadowZagrosDev/Zag-NetStats/releases/download/v${netStatsVersion}/zag-netStats-`;
const proxyResetUrlBase = `https://github.com/ircfspace/proxyReset/releases/download/v${proxyResetVersion}/proxy-reset-`;
const masquePlusUrlBase = `https://github.com/ircfspace/masque-plus/releases/download/v${mpVersion}/masque-plus-`;

const warpPlusUrls: Record<string, Record<string, string>> = {
    linux: {
        x64: warpPlusUrlBase + 'linux-amd64.zip',
        arm64: warpPlusUrlBase + 'linux-arm64.zip'
    },
    win32: {
        x64: warpPlusUrlBase + 'windows-amd64.zip',
        arm64: warpPlusUrlBase + 'windows-arm64.zip',
        ia32: warpPlusUrlBase + 'windows-386.zip'
    },
    darwin: {
        x64: warpPlusUrlBase + 'darwin-amd64.zip',
        arm64: warpPlusUrlBase + 'darwin-arm64.zip'
    }
};

const helperUrls: Record<string, Record<string, string>> = {
    linux: {
        x64: helperUrlBase + 'linux-amd64.zip',
        arm64: helperUrlBase + 'linux-arm64.zip'
    },
    win32: {
        x64: helperUrlBase + 'windows-amd64.zip',
        arm64: helperUrlBase + 'windows-arm64.zip',
        ia32: helperUrlBase + 'windows-386.zip'
    },
    darwin: {
        x64: helperUrlBase + 'darwin-amd64.zip',
        arm64: helperUrlBase + 'darwin-arm64.zip'
    }
};

const netStatsUrls: Record<string, Record<string, string>> = {
    linux: {
        x64: netStatsUrlBase + 'linux-amd64.zip',
        arm64: netStatsUrlBase + 'linux-arm64.zip'
    },
    win32: {
        x64: netStatsUrlBase + 'windows-amd64.zip',
        arm64: netStatsUrlBase + 'windows-arm64.zip',
        ia32: netStatsUrlBase + 'windows-386.zip'
    },
    darwin: {
        x64: netStatsUrlBase + 'darwin-amd64.zip',
        arm64: netStatsUrlBase + 'darwin-arm64.zip'
    }
};

const proxyResetUrls: Record<string, Record<string, string>> = {
    win32: {
        x64: proxyResetUrlBase + 'x64.zip',
        arm64: proxyResetUrlBase + 'arm64.zip',
        ia32: proxyResetUrlBase + 'ia32.zip'
    }
};

const masquePlusUrls: Record<string, Record<string, string>> = {
    linux: {
        x64: masquePlusUrlBase + 'linux_amd64.zip',
        arm64: masquePlusUrlBase + 'linux_arm64.zip'
    },
    win32: {
        x64: masquePlusUrlBase + 'windows_amd64.zip',
        arm64: masquePlusUrlBase + 'windows_arm64.zip',
        ia32: masquePlusUrlBase + 'windows_amd64.zip' // This architecture is not supported, but it is listed to prevent errors
    },
    darwin: {
        x64: masquePlusUrlBase + 'darwin_amd64.zip',
        arm64: masquePlusUrlBase + 'darwin_arm64.zip'
    }
};

const removeFile = async (filePath: string) => {
    const isExist = await doesFileExist(filePath);
    if (isExist) {
        fs.rm(filePath, (err) => {
            if (err) console.error(`Error removing ${filePath}:`, err);
        });
    }
};

async function handleDownload() {
    await dlUnzipMove(warpPlusUrls[platform][arch], './assets/bin', `warp-plus-v${wpVersion}.zip`);
    await removeFile('./assets/bin/wintun.dll');

    await dlUnzipMove(
        helperUrls[platform][arch],
        './assets/bin/oblivion-helper',
        `oblivion-helper-v${helperVersion}.zip`
    );

    await dlUnzipMove(
        netStatsUrls[platform][arch],
        './assets/bin',
        `zag-netStats-v${netStatsVersion}.zip`
    );

    if (platform === 'win32') {
        await dlUnzipMove(
            proxyResetUrls[platform][arch],
            './assets/bin',
            `proxy-reset-v${proxyResetVersion}.zip`
        );
    }

    await dlUnzipMove(
        masquePlusUrls[platform][arch],
        './assets/bin',
        `masque-plus-v${mpVersion}.zip`
    );
}

const notSupported = () => console.log('Your platform/architecture is not supported.');

switch (platform) {
    case 'linux':
    case 'win32':
    case 'darwin':
        switch (arch) {
            case 'x64':
            case 'arm64':
            case 'ia32':
                handleDownload().catch(notSupported);
                break;

            default:
                notSupported();
                break;
        }
        break;

    default:
        notSupported();
        break;
}


================================================
FILE: script/makeRegeditVBSAvailable.ts
================================================
// https://www.npmjs.com/package/regedit#a-note-about-electron

import fs from 'fs';

(async () => {
    const vbsAssetsPath = './node_modules/regedit/vbs';
    const vbsDirPath = './assets/bin/vbs';

    if (process.platform === 'win32') {
        fs.mkdir(vbsDirPath, { recursive: true }, (err) => {
            if (err) {
                console.error(`Error creating directory ${vbsDirPath}:`, err);
            }
            fs.cp(vbsAssetsPath, vbsDirPath, { recursive: true }, (err2) => {
                if (err2) throw err2;
                console.log('✅ regedit wsf files are ready to use.\n');
            });
        });
    }
})();


================================================
FILE: script/playground.ts
================================================
import { doesFileExist } from '../src/main/lib/utils';

(async () => {
    const tmp = await doesFileExist('bin');
    console.log('🚀 - tmp:', tmp);
})();


================================================
FILE: script/postinstall.ts
================================================
import { exec } from 'child_process';

(async () => {
    if (process.platform === 'win32') {
        exec('npm run postinstall:windows"', (err, stdout) => {
            if (err) {
                console.error(err);
                return;
            }
            console.log(stdout);
        });
    } else {
        exec('npm run postinstall:darwin-linux', (err, stdout) => {
            if (err) {
                console.error(err);
                return;
            }
            console.log(stdout);
        });
    }
})();


================================================
FILE: src/__tests__/App.test.tsx
================================================
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import App from '../renderer/App';

describe('App', () => {
    it('should render', () => {
        expect(render(<App />)).toBeTruthy();
    });
});


================================================
FILE: src/constants.ts
================================================
import { app } from 'electron';
import path from 'path';
import SingBoxManager from './main/lib/sbManager';
import NetStatsManager from './main/lib/netStatsManager';
import SpeedTestManager from './main/lib/speedTestManager';

//Platforms
export const isWindows = process.platform === 'win32';
export const isLinux = process.platform === 'linux';
export const isDarwin = process.platform === 'darwin';

// Constants
export const appVersion = app.getVersion();
export const wpFileName = `warp-plus${isWindows ? '.exe' : ''}`;
export const helperFileName = `oblivion-helper${isWindows ? '.exe' : ''}`;
export const netStatsFileName = `zag-netStats${isWindows ? '.exe' : ''}`;
export const proxyResetFileName = `proxy-reset${isWindows ? '.exe' : ''}`;
export const usqueFileName = `usque${isWindows ? '.exe' : ''}`;
export const mpFileName = `masque-plus${isWindows ? '.exe' : ''}`;
export const sbConfigName = 'sbConfig.json';
export const sbExportListName = 'sbExportList.json';
export const sbCacheName = 'sbCache.db';
export const sbLogName = 'sing-box.log';
export const protoName = `oblivion.proto`;
export const ruleSetBaseUrl =
    'https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/';

// Paths
const appPath = app.getAppPath().replace('/app.asar', '').replace('\\app.asar', '');
export const binAssetPath = path.join(appPath, 'assets', 'bin');
export const wpAssetPath = path.join(binAssetPath, wpFileName);
export const helperAssetPath = path.join(binAssetPath, 'oblivion-helper', helperFileName);
export const netStatsAssetPath = path.join(binAssetPath, netStatsFileName);
export const proxyResetAssetPath = path.join(binAssetPath, proxyResetFileName);
export const usqueAssetPath = path.join(binAssetPath, usqueFileName);
export const mpAssetPath = path.join(binAssetPath, mpFileName);
export const regeditVbsDirPath = path.join(binAssetPath, 'vbs');
export const protoAssetPath = path.join(appPath, 'assets', 'proto', protoName);

export const workingDirPath = app.getPath('userData');
export const wpBinPath = path.join(workingDirPath, wpFileName);
export const helperPath = path.join(workingDirPath, helperFileName);
export const sbConfigPath = path.join(workingDirPath, sbConfigName);
export const sbExportListPath = path.join(workingDirPath, sbExportListName);
export const sbLogPath = path.join(workingDirPath, sbLogName);
export const sbCachePath = path.join(workingDirPath, sbCacheName);
export const ruleSetDirPath = path.join(workingDirPath, 'ruleset');
export const netStatsPath = path.join(workingDirPath, netStatsFileName);
export const proxyResetPath = path.join(workingDirPath, proxyResetFileName);
export const usquePath = path.join(workingDirPath, usqueFileName);
export const mpPath = path.join(workingDirPath, mpFileName);
export const versionFilePath = path.join(workingDirPath, 'ver.txt');
export const stuffPath = path.join(workingDirPath, 'stuff');
export const logPath = path.join(app?.getPath('logs'), 'main.log');
export const soundEffect = path.join(appPath, 'assets', 'sound', 'notification.wav');
export const exclusionsPath = path.join(app?.getPath('temp'), 'exclusions.bat');
export const downloadedPath = path.join(app?.getPath('temp'), `oblivion-temp`);
export const updaterPath = path.join(workingDirPath, `oblivion-updater${isWindows ? '.exe' : ''}`);
export const windowPosition = path.join(workingDirPath, 'windowPosition.json');

// Managers
export const singBoxManager = new SingBoxManager();
export const netStatsManager = new NetStatsManager();
export const speedTestManager = new SpeedTestManager();

//Interfaces
export interface INetStats {
    sentSpeed: { value: number; unit: string };
    recvSpeed: { value: number; unit: string };
    totalSent: { value: number; unit: string };
    totalRecv: { value: number; unit: string };
    totalUsage: { value: number; unit: string };
}

export interface IConfig {
    socksIp: string;
    socksPort: number;
    tunMtu: number;
    logLevel: string;
    tunStack: string;
    tunSniff: boolean;
    plainDns: string;
    DoHDns: string;
    tunEndpoint: string;
    tunAddr: string[];
    udpBlock: boolean;
    discordBypass: boolean;
}

export interface IGeoConfig {
    geoIp: string;
    geoSite: string;
    geoBlock: boolean;
    geoNSFW: boolean;
}

export interface IRoutingRules {
    ipSet: string[];
    domainSet: string[];
    domainSuffixSet: string[];
    processSet: string[];
}

export interface ICommand {
    command: string;
    args?: string[];
}

export interface IPlatformHelper {
    start(binPath: string): ICommand;
    running(processName: string): ICommand;
}

//Lists
export const defaultWarpIPs = [
    '162.159.192.0/24',
    '162.159.193.0/24',
    '162.159.195.0/24',
    '188.114.96.0/24',
    '188.114.97.0/24',
    '188.114.98.0/24',
    '188.114.99.0/24',
    '2606:4700:d0::/64',
    '2606:4700:d1::/64'
];

export const gtk4Paths = [
    '/usr/lib/libgtk-4.so',
    '/usr/lib/libgtk-4.so.1',
    '/usr/lib64/libgtk-4.so',
    '/usr/lib64/libgtk-4.so.1',
    '/usr/lib/x86_64-linux-gnu/libgtk-4.so',
    '/usr/lib/x86_64-linux-gnu/libgtk-4.so.1',
    '/usr/local/lib/libgtk-4.so',
    '/usr/local/lib/libgtk-4.so.1'
];


================================================
FILE: src/defaultSettings.ts
======================
Download .txt
gitextract_gl2ervna/

├── .erb/
│   ├── configs/
│   │   ├── .eslintrc
│   │   ├── webpack.config.base.ts
│   │   ├── webpack.config.eslint.ts
│   │   ├── webpack.config.main.dev.ts
│   │   ├── webpack.config.main.prod.ts
│   │   ├── webpack.config.preload.dev.ts
│   │   ├── webpack.config.renderer.dev.dll.ts
│   │   ├── webpack.config.renderer.dev.ts
│   │   ├── webpack.config.renderer.prod.ts
│   │   └── webpack.paths.ts
│   ├── mocks/
│   │   └── fileMock.js
│   └── scripts/
│       ├── .eslintrc
│       ├── check-build-exists.ts
│       ├── check-native-dep.js
│       ├── check-node-env.js
│       ├── check-port-in-use.js
│       ├── clean.js
│       ├── delete-source-maps.js
│       ├── electron-rebuild.js
│       ├── link-modules.ts
│       └── notarize.js
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yaml
│   │   ├── config.yml
│   │   └── feature_request.yaml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── config.yml
│   ├── dependabot.yml
│   ├── release_message.md
│   ├── stale.yml
│   └── workflows/
│       ├── deploy-ppa.yml
│       └── publish.yml
├── .gitignore
├── .husky/
│   ├── pre-commit
│   └── pre-push
├── .prettierignore
├── .prettierrc
├── .vscode/
│   ├── launch.json
│   └── settings.json
├── @types/
│   └── node-aplay.d.ts
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DOCS.md
├── FAQ.md
├── LICENSE.md
├── README-fa.md
├── README.md
├── SECURITY.md
├── assets/
│   ├── assets.d.ts
│   ├── css/
│   │   ├── materialIcons.css
│   │   ├── noto.css
│   │   ├── shabnam.css
│   │   └── style.css
│   ├── entitlements.mac.plist
│   ├── icon.icns
│   ├── json/
│   │   └── 1713988096625.json
│   └── proto/
│       └── oblivion.proto
├── package.json
├── release/
│   └── app/
│       └── package.json
├── script/
│   ├── beforePackHook.js
│   ├── changeVersion.ts
│   ├── dlBins.ts
│   ├── makeRegeditVBSAvailable.ts
│   ├── playground.ts
│   └── postinstall.ts
├── src/
│   ├── __tests__/
│   │   └── App.test.tsx
│   ├── constants.ts
│   ├── defaultSettings.ts
│   ├── localization/
│   │   ├── am.ts
│   │   ├── ar.ts
│   │   ├── cn.ts
│   │   ├── electron.ts
│   │   ├── en.ts
│   │   ├── es.ts
│   │   ├── fa.ts
│   │   ├── id.ts
│   │   ├── index.ts
│   │   ├── my.ts
│   │   ├── pt.ts
│   │   ├── ru.ts
│   │   ├── tr.ts
│   │   ├── type.ts
│   │   ├── ur.ts
│   │   ├── useTranslate.ts
│   │   └── vi.ts
│   ├── main/
│   │   ├── config.ts
│   │   ├── dxConfig.ts
│   │   ├── ipc.ts
│   │   ├── ipcListeners/
│   │   │   ├── log.ts
│   │   │   └── settings.ts
│   │   ├── lib/
│   │   │   ├── customEvent.ts
│   │   │   ├── netStatsManager.ts
│   │   │   ├── pacScript.ts
│   │   │   ├── proxy.ts
│   │   │   ├── sbConfig.ts
│   │   │   ├── sbHelper.ts
│   │   │   ├── sbManager.ts
│   │   │   ├── speedTestManager.ts
│   │   │   ├── utils.ts
│   │   │   ├── wpHelper.ts
│   │   │   └── wpManager.ts
│   │   ├── main.ts
│   │   ├── menu.ts
│   │   ├── playground.ts
│   │   └── preload.ts
│   ├── renderer/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── BackButton.tsx
│   │   │   ├── Card/
│   │   │   │   └── index.tsx
│   │   │   ├── ConfigHandler.tsx
│   │   │   ├── Dropdown/
│   │   │   │   └── index.tsx
│   │   │   ├── Input/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useInput.ts
│   │   │   ├── Modal/
│   │   │   │   ├── DNS/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useDnsModal.ts
│   │   │   │   ├── Endpoint/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useEndpointModal.ts
│   │   │   │   ├── License/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useLicenseModal.ts
│   │   │   │   ├── MTU/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useMTUModal.ts
│   │   │   │   ├── Port/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── usePortModal.ts
│   │   │   │   ├── Profile/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useProfileModal.ts
│   │   │   │   ├── Restore/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useRestoreModal.ts
│   │   │   │   ├── RoutingRules/
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── useRoutingRulesModal.ts
│   │   │   │   └── TestUrl/
│   │   │   │       ├── index.tsx
│   │   │   │       └── useTestUrlModal.ts
│   │   │   ├── Nav/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useNav.ts
│   │   │   ├── Tabs.tsx
│   │   │   └── Textarea/
│   │   │       ├── index.tsx
│   │   │       └── useTextarea.ts
│   │   ├── context/
│   │   │   └── GlobalContext.tsx
│   │   ├── hooks/
│   │   │   ├── useButtonKeyDown.ts
│   │   │   └── useGoBackOnEscape.tsx
│   │   ├── index.ejs
│   │   ├── index.tsx
│   │   ├── lib/
│   │   │   ├── cfFlag.ts
│   │   │   ├── dx.ts
│   │   │   ├── getIspName.ts
│   │   │   ├── globalEvents.ts
│   │   │   ├── inputSanitizer.ts
│   │   │   ├── isAnyUndefined.ts
│   │   │   ├── loaders.ts
│   │   │   ├── settings.ts
│   │   │   ├── systemDateValidator.ts
│   │   │   ├── toPersianNumber.ts
│   │   │   ├── toasts.tsx
│   │   │   ├── utils.ts
│   │   │   └── withDefault.ts
│   │   ├── pages/
│   │   │   ├── About/
│   │   │   │   └── index.tsx
│   │   │   ├── Debug/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useDebug.ts
│   │   │   ├── Landing/
│   │   │   │   ├── DownloadProgressBar.tsx
│   │   │   │   ├── LandingBody.tsx
│   │   │   │   ├── LandingDrawer.tsx
│   │   │   │   ├── LandingHeader.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── useLanding.ts
│   │   │   ├── Network/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useOptions.ts
│   │   │   ├── Options/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useOptions.ts
│   │   │   ├── Scanner/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useScanner.ts
│   │   │   ├── Settings/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useSettings.ts
│   │   │   ├── SingBox/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useSingBox.ts
│   │   │   ├── SpeedTest/
│   │   │   │   ├── index.tsx
│   │   │   │   └── useSpeedTest.ts
│   │   │   └── SplashScreen/
│   │   │       ├── index.tsx
│   │   │       └── useSplashScreen.ts
│   │   ├── preload.d.ts
│   │   ├── routes/
│   │   │   └── index.tsx
│   │   └── store.ts
│   └── types/
│       └── global.d.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (274 symbols across 84 files)

FILE: .erb/configs/webpack.config.renderer.dev.ts
  method setupMiddlewares (line 180) | setupMiddlewares(middlewares) {

FILE: .erb/scripts/check-node-env.js
  function checkNodeEnv (line 3) | function checkNodeEnv(expectedEnv) {

FILE: .erb/scripts/delete-source-maps.js
  function deleteSourceMaps (line 6) | function deleteSourceMaps() {

FILE: @types/node-aplay.d.ts
  class Aplay (line 2) | class Aplay {

FILE: assets/assets.d.ts
  type Styles (line 1) | type Styles = Record<string, string>;

FILE: script/changeVersion.ts
  function changeJson (line 10) | function changeJson(filePath: string, key: string, value: string, callba...

FILE: script/dlBins.ts
  function downloadFile (line 20) | async function downloadFile(uri: string, destPath: string) {
  function dlUnzipMove (line 52) | async function dlUnzipMove(url: string, binPath: string, zipFileName: st...
  function handleDownload (line 163) | async function handleDownload() {

FILE: src/constants.ts
  type INetStats (line 67) | interface INetStats {
  type IConfig (line 75) | interface IConfig {
  type IGeoConfig (line 90) | interface IGeoConfig {
  type IRoutingRules (line 97) | interface IRoutingRules {
  type ICommand (line 104) | interface ICommand {
  type IPlatformHelper (line 109) | interface IPlatformHelper {

FILE: src/defaultSettings.ts
  type settingsKeys (line 3) | type settingsKeys =

FILE: src/localization/electron.ts
  type LanguageType (line 18) | type LanguageType =

FILE: src/localization/index.ts
  type LanguageType (line 16) | type LanguageType =
  type directionType (line 30) | type directionType = 'rtl' | 'ltr';

FILE: src/localization/type.ts
  type Global (line 1) | interface Global {}
  type Status (line 3) | interface Status {
  type Home (line 15) | interface Home {
  type Toast (line 31) | interface Toast {
  type Settings (line 51) | interface Settings {
  type Tabs (line 153) | interface Tabs {
  type Modal (line 162) | interface Modal {
  type Log (line 195) | interface Log {
  type About (line 231) | interface About {
  type SystemTray (line 237) | interface SystemTray {
  type Update (line 253) | interface Update {
  type SpeedTest (line 260) | interface SpeedTest {
  type Language (line 272) | interface Language {

FILE: src/localization/useTranslate.ts
  type LanguageType (line 18) | type LanguageType =

FILE: src/main/ipcListeners/log.ts
  function readLogFile (line 25) | function readLogFile(value: string) {

FILE: src/main/lib/netStatsManager.ts
  class NetStatsManager (line 7) | class NetStatsManager {
    method constructor (line 20) | constructor(private readonly spawnArgs: string[] = ['-t', '1', '-p', '...
    method initializeIpcEvents (line 27) | public initializeIpcEvents(): void {
    method initializeNetworkInterface (line 39) | private async initializeNetworkInterface(): Promise<void> {
    method startMonitoring (line 43) | public startMonitoring(event: any): void {
    method stopMonitoring (line 90) | public stopMonitoring(): void {

FILE: src/main/lib/proxy.ts
  constant DEFAULT_ROUTING_RULES (line 23) | const DEFAULT_ROUTING_RULES =
  constant REGEDIT_PATH (line 25) | const REGEDIT_PATH = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion...
  function registerStartupProxyReset (line 44) | async function registerStartupProxyReset(): Promise<void> {
  function removeStartupProxyReset (line 70) | async function removeStartupProxyReset(): Promise<void> {

FILE: src/main/lib/sbConfig.ts
  function createSbConfig (line 20) | function createSbConfig(config: IConfig, geoConfig: IGeoConfig, rulesCon...

FILE: src/main/lib/sbHelper.ts
  class WindowsHelper (line 4) | class WindowsHelper implements IPlatformHelper {
    method start (line 5) | start(binPath: string): ICommand {
    method running (line 15) | running(): ICommand {
  class DarwinHelper (line 22) | class DarwinHelper implements IPlatformHelper {
    method start (line 23) | start(binPath: string): ICommand {
    method running (line 33) | running(processName: string): ICommand {
  class LinuxHelper (line 40) | class LinuxHelper implements IPlatformHelper {
    method start (line 41) | start(binPath: string): ICommand {
    method running (line 48) | running(processName: string): ICommand {
  class RoutingRuleParser (line 55) | class RoutingRuleParser {
    method parse (line 56) | parse(routingRules: any): IRoutingRules {
    method processRule (line 82) | private processRule(prefix: string, value: string, result: IRoutingRul...
    method processDomainRule (line 98) | private processDomainRule(value: string, result: IRoutingRules): void {
    method processAppRule (line 106) | private processAppRule(value: string, result: IRoutingRules): void {

FILE: src/main/lib/sbManager.ts
  type GrpcMethod (line 38) | type GrpcMethod = 'Start' | 'Stop';
  type GrpcResponse (line 39) | interface GrpcResponse<T> {
  type GrpcError (line 42) | interface GrpcError {
  constant CONFIG (line 48) | const CONFIG = {
  class SingBoxManager (line 65) | class SingBoxManager {
    method constructor (line 84) | constructor() {
    method startSingBox (line 91) | public async startSingBox(appLang?: Language, event?: IpcMainEvent): P...
    method stopSingBox (line 112) | public async stopSingBox(): Promise<boolean> {
    method stopHelper (line 126) | public async stopHelper(): Promise<void> {
    method stopHelperOnStart (line 141) | public async stopHelperOnStart(): Promise<void> {
    method checkConnectionStatus (line 157) | public async checkConnectionStatus(): Promise<boolean> {
    method createGrpcClient (line 199) | private createGrpcClient() {
    method ensureHelperIsRunning (line 214) | private async ensureHelperIsRunning(): Promise<boolean> {
    method startHelper (line 218) | private async startHelper(): Promise<boolean> {
    method createPlatformHelper (line 277) | private createPlatformHelper(): IPlatformHelper {
    method setupConfigs (line 288) | private async setupConfigs(): Promise<void> {
    method loadConfiguration (line 302) | private async loadConfiguration(): Promise<IConfig> {
    method loadGeoConfiguration (line 352) | private async loadGeoConfiguration(): Promise<IGeoConfig> {
    method getPlainDns (line 368) | private getPlainDns(dns: any, plainDns: any): string {
    method getDoHDns (line 375) | private getDoHDns(dns: any, doh: any): string {
    method getTunAddr (line 382) | private getTunAddr(addrType: any): string[] {
    method getSettingOrDefault (line 395) | private getSettingOrDefault<T>(value: any, defaultValue: T): T {
    method setupGeoLists (line 399) | private async setupGeoLists(geoConfig: IGeoConfig): Promise<void> {
    method monitorHelperStatus (line 421) | private async monitorHelperStatus(): Promise<void> {
    method handleConnectionFailure (line 461) | private handleConnectionFailure(): void {
    method delay (line 467) | private delay(ms: number): Promise<void> {
    method replyEvent (line 473) | private replyEvent(message: string): void {
    method isProcessRunning (line 477) | private isProcessRunning(processName: string): boolean {
    method executeGrpcCall (line 484) | private async executeGrpcCall<T>(method: GrpcMethod, request: any): Pr...
    method startService (line 517) | private startService(): Promise<boolean> {
    method stopService (line 523) | private stopService(): Promise<boolean> {

FILE: src/main/lib/speedTestManager.ts
  class SpeedTestManager (line 16) | class SpeedTestManager {
    method constructor (line 21) | constructor() {
    method initializeIpcEvents (line 25) | public initializeIpcEvents(): void {
    method setupSpeedTest (line 52) | private setupSpeedTest() {
    method broadcastResults (line 66) | private broadcastResults(status: string) {

FILE: src/main/lib/utils.ts
  function doesFileExist (line 16) | function doesFileExist(filePath: string) {
  function removeFileIfExists (line 33) | function removeFileIfExists(filePath: string) {
  function removeDirIfExists (line 49) | function removeDirIfExists(dirPath: string) {
  function shouldProxySystem (line 63) | function shouldProxySystem(proxyMode: any) {
  function hasLicense (line 67) | function hasLicense(license: any) {
  function checkRoutingRules (line 71) | function checkRoutingRules(value: any) {
  function checkEndpoint (line 75) | function checkEndpoint(endpoint: any) {
  function checkDataUsage (line 82) | function checkDataUsage(value: any) {
  function checkProxyMode (line 92) | function checkProxyMode(value: any) {
  function checkReserved (line 96) | function checkReserved(value: any) {
  function checkGeoStatus (line 106) | function checkGeoStatus(ip: any, site: any, block: any, nsfw: any) {
  function calculateMethod (line 132) | function calculateMethod(method: any) {
  function isSocksProxy (line 148) | function isSocksProxy(method: any) {
  function checkIpType (line 162) | function checkIpType(value: any, endpoint: any) {
  function checkTunAddrType (line 181) | function checkTunAddrType(addrType: any): string {
  function checkTestUrl (line 187) | function checkTestUrl(value: any) {
  function checkDNS (line 191) | function checkDNS(value: any) {
  function extractPortsFromEndpoints (line 216) | function extractPortsFromEndpoints(strData: string): number[] {
  function formatEndpointForConfig (line 242) | function formatEndpointForConfig(endpoint: string): string {
  function mapGrpcErrorCodeToLabel (line 248) | function mapGrpcErrorCodeToLabel(code: number | undefined): string {
  function isIpBasedDoH (line 292) | function isIpBasedDoH(url: string): boolean {
  function versionComparison (line 306) | function versionComparison(localVersion: any, apiVersion: any): boolean {

FILE: src/main/lib/wpManager.ts
  type ConnectionState (line 37) | enum ConnectionState {
  type WarpPlusState (line 44) | interface WarpPlusState {
  constant MAX_RETRIES (line 68) | const MAX_RETRIES = 2;
  constant SUCCESS_MSG_PREFIX (line 69) | const SUCCESS_MSG_PREFIX = 'level=INFO msg="serving proxy" address=';
  class WarpPlusManager (line 88) | class WarpPlusManager {
    method restartApp (line 90) | static restartApp(delay = 5000) {
    method getWinDrive (line 114) | private static getWinDrive() {
    method addToExclusions (line 125) | static async addToExclusions() {
    method handleSystemProxy (line 230) | static async handleSystemProxy(enable: boolean) {
    method handleMissingFile (line 251) | static async handleMissingFile(assetPath: string, errorMsg: string) {
    method startWarpPlus (line 265) | static async startWarpPlus() {
    method killChild (line 306) | static killChild() {
    method playSoundEffect (line 312) | private static playSoundEffect() {
    method sendConnectionSignal (line 327) | private static sendConnectionSignal() {
    method setupChildProcessHandlers (line 363) | private static setupChildProcessHandlers() {
    method handleSuccessMessage (line 390) | private static async handleSuccessMessage() {
    method handleEndpointUpdates (line 401) | private static async handleEndpointUpdates(strData: string) {
    method handleChildExit (line 408) | private static async handleChildExit() {
    method handleStartupError (line 430) | private static handleStartupError() {

FILE: src/main/main.ts
  constant APP_TITLE (line 66) | const APP_TITLE = `Oblivion Desktop${isDev() ? ' ᴅᴇᴠ' : ''}`;
  constant WINDOW_DIMENSIONS (line 67) | const WINDOW_DIMENSIONS = {
  type WindowState (line 97) | interface WindowState {
  class OblivionDesktop (line 112) | class OblivionDesktop {
    method constructor (line 127) | constructor() {
    method initialize (line 131) | private async initialize(): Promise<void> {
    method setupInitialConfiguration (line 161) | private async setupInitialConfiguration(): Promise<void> {
    method handleVersionCheck (line 169) | private async handleVersionCheck(): Promise<void> {
    method cleanupOldFiles (line 198) | private async cleanupOldFiles(): Promise<void> {
    method copyRequiredFiles (line 210) | private copyRequiredFiles(): void {
    method getLastWindowPosition (line 237) | private getLastWindowPosition(): { x: number; y: number } | null {
    method saveWindowPosition (line 250) | private saveWindowPosition(x: number, y: number) {
    method getValidWindowPosition (line 255) | private getValidWindowPosition(x: number, y: number): { x: number; y: ...
    method createWindowConfig (line 287) | private createWindowConfig(): BrowserWindowConstructorOptions {
    method getAssetPath (line 330) | private getAssetPath(...paths: string[]): string {
    method resolveHtmlPath (line 337) | private resolveHtmlPath(htmlFileName: string): string {
    method createWindow (line 347) | private async createWindow(): Promise<void> {
    method openDevTools (line 395) | private openDevTools(): void {
    method setupWindowEvents (line 404) | private async setupWindowEvents(): Promise<void> {
    method exitProcess (line 451) | private async exitProcess() {
    method handleWindowClose (line 466) | private async handleWindowClose(e: Event): Promise<void> {
    method registerQuitShortcut (line 483) | private registerQuitShortcut(): void {
    method unregisterQuitShortcut (line 490) | private unregisterQuitShortcut(): void {
    method checkForUpdates (line 495) | private async checkForUpdates(downloadUpdate?: boolean) {
    method resetCheckForUpdatesInterval (line 643) | private resetCheckForUpdatesInterval(): void {
    method setupCheckForUpdates (line 653) | private setupCheckForUpdates(): void {
    method setupIpcEvents (line 665) | private setupIpcEvents(): void {
    method downloadUpdate (line 716) | private async downloadUpdate(
    method onDownloadError (line 798) | private onDownloadError() {
    method setupAppEvents (line 806) | private setupAppEvents(): void {
    method setupTray (line 876) | private async setupTray(): Promise<void> {
    method createTrayIcon (line 898) | private createTrayIcon(status: string): NativeImage {
    method updateTrayMenu (line 907) | private updateTrayMenu(): void {
    method createTrayMenuTemplate (line 918) | private createTrayMenuTemplate(): MenuItemConstructorOptions[] {
    method getConnectionLabel (line 1052) | private getConnectionLabel(): string {
    method handleConnectionToggle (line 1064) | private handleConnectionToggle(): void {
    method redirectTo (line 1074) | private redirectTo(route: string): void {
    method checkStartUp (line 1092) | private async checkStartUp(): Promise<void> {
    method autoConnect (line 1108) | private async autoConnect(): Promise<void> {
    method setupMetaData (line 1120) | private async setupMetaData(): Promise<void> {
    method getNetworkList (line 1161) | private async getNetworkList() {
    method sleep (line 1171) | private sleep(ms: number): Promise<void> {
    method handleAppReady (line 1176) | public async handleAppReady(): Promise<void> {

FILE: src/main/menu.ts
  class MenuBuilder (line 5) | class MenuBuilder {
    method constructor (line 8) | constructor(mainWindow: BrowserWindow) {
    method buildMenu (line 12) | buildMenu(): Menu {
    method setupDevelopmentEnvironment (line 25) | setupDevelopmentEnvironment(): void {
    method buildDefaultTemplate (line 40) | buildDefaultTemplate() {

FILE: src/main/playground.ts
  function main (line 36) | async function main() {

FILE: src/main/preload.ts
  type Channels (line 3) | type Channels =
  method sendMessage (line 27) | sendMessage(channel: Channels, ...args: any[]) {
  method on (line 30) | on(
  method once (line 39) | once(
  method removeListener (line 48) | removeListener(channel: Channels, func: (...args: any[]) => void) {
  method removeAllListeners (line 51) | removeAllListeners(channel: Channels) {
  method invoke (line 54) | invoke(channel: Channels, ...args: any[]) {
  method clean (line 57) | clean() {
  type ElectronHandler (line 80) | type ElectronHandler = typeof electronHandler;

FILE: src/renderer/App.tsx
  function App (line 16) | function App() {

FILE: src/renderer/components/BackButton.tsx
  function BackButton (line 4) | function BackButton() {

FILE: src/renderer/components/Card/index.tsx
  type ResultCardProps (line 3) | interface ResultCardProps {

FILE: src/renderer/components/ConfigHandler.tsx
  type ConfigHandlerProps (line 5) | interface ConfigHandlerProps {

FILE: src/renderer/components/Dropdown/index.tsx
  type DropdownItem (line 3) | type DropdownItem = {
  type DropdownProps (line 8) | interface DropdownProps {

FILE: src/renderer/components/Input/index.tsx
  type InputProps (line 4) | interface InputProps {

FILE: src/renderer/components/Input/useInput.ts
  type ContextMenuStyleType (line 3) | type ContextMenuStyleType = {

FILE: src/renderer/components/Modal/DNS/index.tsx
  type DnsModalProps (line 5) | interface DnsModalProps {
  function DnsModal (line 16) | function DnsModal({

FILE: src/renderer/components/Modal/DNS/useDnsModal.ts
  type DnsModalProps (line 5) | interface DnsModalProps {

FILE: src/renderer/components/Modal/Endpoint/index.tsx
  type EndpointModalProps (line 8) | interface EndpointModalProps {

FILE: src/renderer/components/Modal/Endpoint/useEndpointModal.ts
  type EndpointModalProps (line 11) | type EndpointModalProps = {
  type Method (line 20) | type Method = keyof Suggestion;
  type Suggestion (line 21) | type Suggestion = {

FILE: src/renderer/components/Modal/License/index.tsx
  type LicenseModalProps (line 5) | interface LicenseModalProps {
  function LicenseModal (line 13) | function LicenseModal({

FILE: src/renderer/components/Modal/License/useLicenseModal.ts
  type LicenseModalProps (line 9) | interface LicenseModalProps {

FILE: src/renderer/components/Modal/MTU/index.tsx
  type MTUModalProps (line 6) | interface MTUModalProps {
  function MTUModal (line 15) | function MTUModal({

FILE: src/renderer/components/Modal/MTU/useMTUModal.ts
  type MtuModalProps (line 9) | interface MtuModalProps {

FILE: src/renderer/components/Modal/Port/index.tsx
  type PortModalProps (line 5) | interface PortModalProps {
  function PortModal (line 14) | function PortModal({

FILE: src/renderer/components/Modal/Port/usePortModal.ts
  type PortModalProps (line 9) | interface PortModalProps {

FILE: src/renderer/components/Modal/Profile/index.tsx
  type ProfileModalProps (line 8) | interface ProfileModalProps {

FILE: src/renderer/components/Modal/Profile/useProfileModal.ts
  type ProfileModalProps (line 12) | type ProfileModalProps = {

FILE: src/renderer/components/Modal/Restore/index.tsx
  type RestoreModalProps (line 4) | interface RestoreModalProps {
  function RestoreModal (line 17) | function RestoreModal({

FILE: src/renderer/components/Modal/Restore/useRestoreModal.ts
  type RestoreModalProps (line 19) | interface RestoreModalProps {

FILE: src/renderer/components/Modal/RoutingRules/index.tsx
  type RoutingRulesModalProps (line 5) | interface RoutingRulesModalProps {
  function RoutingRulesModal (line 14) | function RoutingRulesModal({

FILE: src/renderer/components/Modal/RoutingRules/useRoutingRulesModal.ts
  type RoutingRulesModalProps (line 9) | interface RoutingRulesModalProps {

FILE: src/renderer/components/Modal/TestUrl/index.tsx
  type TestUrlModalProps (line 6) | interface TestUrlModalProps {
  function TestUrlModal (line 14) | function TestUrlModal({

FILE: src/renderer/components/Modal/TestUrl/useTestUrlModal.ts
  type TestUrlModalProps (line 9) | interface TestUrlModalProps {

FILE: src/renderer/components/Nav/index.tsx
  type NavProps (line 5) | interface NavProps {

FILE: src/renderer/components/Tabs.tsx
  type TabsProps (line 5) | interface TabsProps {
  function Tabs (line 10) | function Tabs({ active, proxyMode }: TabsProps) {

FILE: src/renderer/components/Textarea/index.tsx
  type InputProps (line 4) | interface InputProps {

FILE: src/renderer/components/Textarea/useTextarea.ts
  type ContextMenuStyleType (line 3) | type ContextMenuStyleType = {

FILE: src/renderer/hooks/useGoBackOnEscape.tsx
  function useGoBackOnEscape (line 5) | function useGoBackOnEscape() {

FILE: src/renderer/lib/dx.ts
  function onKeyDown (line 9) | function onKeyDown(event: KeyboardEvent) {

FILE: src/renderer/lib/getIspName.ts
  function getIspName (line 49) | async function getIspName() {

FILE: src/renderer/lib/globalEvents.ts
  function globalEvents (line 17) | function globalEvents() {

FILE: src/renderer/lib/inputSanitizer.ts
  type ConfigType (line 8) | type ConfigType =
  constant UNTITLED (line 27) | const UNTITLED = 'untitled';
  constant VALID_COUNTRIES (line 30) | const VALID_COUNTRIES = countries.map((c) => c.value);

FILE: src/renderer/lib/isAnyUndefined.ts
  function isAnyUndefined (line 1) | function isAnyUndefined(...values: unknown[]): boolean {
  function typeIsUndefined (line 5) | function typeIsUndefined(value: unknown): boolean {
  function typeIsNotUndefined (line 9) | function typeIsNotUndefined(value: unknown): boolean {

FILE: src/renderer/lib/loaders.ts
  type SettingsKeys (line 34) | type SettingsKeys = keyof typeof defaultSettings;

FILE: src/renderer/lib/settings.ts
  class settings (line 4) | class settings {
    method get (line 5) | public static async get(key: settingsKeys): Promise<any> {
    method getMultiple (line 11) | public static async getMultiple(keys: string[]): Promise<any> {
    method set (line 28) | public static async set(key: settingsKeys, value: any) {

FILE: src/renderer/lib/utils.ts
  function onKeyDown (line 6) | function onKeyDown(event: KeyboardEvent) {

FILE: src/renderer/pages/About/index.tsx
  function About (line 9) | function About() {

FILE: src/renderer/pages/Debug/index.tsx
  function Debug (line 6) | function Debug() {

FILE: src/renderer/pages/Landing/DownloadProgressBar.tsx
  type DownloadProgress (line 4) | type DownloadProgress = {
  type DownloadProgressBarProps (line 9) | interface DownloadProgressBarProps {

FILE: src/renderer/pages/Landing/LandingBody.tsx
  type LandingBodyProps (line 9) | interface LandingBodyProps {

FILE: src/renderer/pages/Landing/LandingDrawer.tsx
  type LandingDrawerProps (line 9) | interface LandingDrawerProps {

FILE: src/renderer/pages/Landing/LandingHeader.tsx
  type LandingHeaderProps (line 7) | interface LandingHeaderProps {

FILE: src/renderer/pages/Landing/index.tsx
  function Landing (line 13) | function Landing() {

FILE: src/renderer/pages/Landing/useLanding.ts
  type IpConfig (line 21) | type IpConfig = {

FILE: src/renderer/pages/Network/index.tsx
  function Network (line 29) | function Network() {

FILE: src/renderer/pages/Options/index.tsx
  function Options (line 12) | function Options() {

FILE: src/renderer/pages/Scanner/index.tsx
  function Scanner (line 10) | function Scanner() {

FILE: src/renderer/pages/Scanner/useScanner.ts
  type Profile (line 13) | type Profile = {

FILE: src/renderer/pages/Settings/index.tsx
  function Settings (line 11) | function Settings() {

FILE: src/renderer/pages/SingBox/index.tsx
  function SingBox (line 16) | function SingBox() {

FILE: src/renderer/pages/SingBox/useSingBox.ts
  type SettingValue (line 16) | type SettingValue = string | number | boolean | null;
  type SettingsState (line 18) | type SettingsState = {

FILE: src/renderer/pages/SpeedTest/useSpeedTest.ts
  type TestResults (line 6) | interface TestResults {
  constant MB_CONVERSION (line 13) | const MB_CONVERSION = 1_000_000;

FILE: src/renderer/pages/SplashScreen/index.tsx
  function SplashScreen (line 3) | function SplashScreen() {

FILE: src/renderer/preload.d.ts
  type Window (line 5) | interface Window {

FILE: src/renderer/store.ts
  type DownloadProgress (line 5) | type DownloadProgress = {
  type IStore (line 10) | interface IStore {

FILE: src/types/global.d.ts
  type Window (line 4) | interface Window {
Condensed preview — 184 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (884K chars).
[
  {
    "path": ".erb/configs/.eslintrc",
    "chars": 114,
    "preview": "{\n  \"rules\": {\n    \"no-console\": \"off\",\n    \"global-require\": \"off\",\n    \"import/no-dynamic-require\": \"off\"\n  }\n}\n"
  },
  {
    "path": ".erb/configs/webpack.config.base.ts",
    "chars": 1628,
    "preview": "/**\n * Base webpack config used across other specific configs\n */\n\nimport webpack from 'webpack';\nimport TsconfigPathsPl"
  },
  {
    "path": ".erb/configs/webpack.config.eslint.ts",
    "chars": 135,
    "preview": "/* eslint import/no-unresolved: off, import/no-self-import: off */\n\nmodule.exports = require('./webpack.config.renderer."
  },
  {
    "path": ".erb/configs/webpack.config.main.dev.ts",
    "chars": 1761,
    "preview": "/**\n * Webpack config for development electron main process\n */\n\nimport path from 'path';\nimport webpack from 'webpack';"
  },
  {
    "path": ".erb/configs/webpack.config.main.prod.ts",
    "chars": 2198,
    "preview": "/**\n * Webpack config for production electron main process\n */\n\nimport path from 'path';\nimport webpack from 'webpack';\n"
  },
  {
    "path": ".erb/configs/webpack.config.preload.dev.ts",
    "chars": 2092,
    "preview": "import path from 'path';\nimport webpack from 'webpack';\nimport { merge } from 'webpack-merge';\nimport { BundleAnalyzerPl"
  },
  {
    "path": ".erb/configs/webpack.config.renderer.dev.dll.ts",
    "chars": 1902,
    "preview": "/**\n * Builds the DLL for development electron renderer process\n */\n\nimport webpack from 'webpack';\nimport path from 'pa"
  },
  {
    "path": ".erb/configs/webpack.config.renderer.dev.ts",
    "chars": 6567,
    "preview": "import 'webpack-dev-server';\nimport path from 'path';\nimport fs from 'fs';\nimport webpack from 'webpack';\nimport HtmlWeb"
  },
  {
    "path": ".erb/configs/webpack.config.renderer.prod.ts",
    "chars": 4120,
    "preview": "/**\n * Build config for electron renderer process\n */\n\nimport path from 'path';\nimport webpack from 'webpack';\nimport Ht"
  },
  {
    "path": ".erb/configs/webpack.paths.ts",
    "chars": 1165,
    "preview": "const path = require('path');\n\nconst rootPath = path.join(__dirname, '../..');\n\nconst erbPath = path.join(__dirname, '.."
  },
  {
    "path": ".erb/mocks/fileMock.js",
    "chars": 33,
    "preview": "export default 'test-file-stub';\n"
  },
  {
    "path": ".erb/scripts/.eslintrc",
    "chars": 162,
    "preview": "{\n  \"rules\": {\n    \"no-console\": \"off\",\n    \"global-require\": \"off\",\n    \"import/no-dynamic-require\": \"off\",\n    \"import"
  },
  {
    "path": ".erb/scripts/check-build-exists.ts",
    "chars": 737,
    "preview": "// Check if the renderer and main bundles are built\nimport path from 'path';\nimport chalk from 'chalk';\nimport fs from '"
  },
  {
    "path": ".erb/scripts/check-native-dep.js",
    "chars": 2143,
    "preview": "import fs from 'fs';\nimport chalk from 'chalk';\nimport { execSync } from 'child_process';\nimport { dependencies } from '"
  },
  {
    "path": ".erb/scripts/check-node-env.js",
    "chars": 423,
    "preview": "import chalk from 'chalk';\n\nexport default function checkNodeEnv(expectedEnv) {\n    if (!expectedEnv) {\n        throw ne"
  },
  {
    "path": ".erb/scripts/check-port-in-use.js",
    "chars": 450,
    "preview": "import chalk from 'chalk';\nimport detectPort from 'detect-port';\n\nconst port = process.env.PORT || '1212';\n\ndetectPort(p"
  },
  {
    "path": ".erb/scripts/clean.js",
    "chars": 301,
    "preview": "import { rimrafSync } from 'rimraf';\nimport fs from 'fs';\nimport webpackPaths from '../configs/webpack.paths';\n\nconst fo"
  },
  {
    "path": ".erb/scripts/delete-source-maps.js",
    "chars": 504,
    "preview": "import fs from 'fs';\nimport path from 'path';\nimport { rimrafSync } from 'rimraf';\nimport webpackPaths from '../configs/"
  },
  {
    "path": ".erb/scripts/electron-rebuild.js",
    "chars": 619,
    "preview": "import { execSync } from 'child_process';\nimport fs from 'fs';\nimport { dependencies } from '../../release/app/package.j"
  },
  {
    "path": ".erb/scripts/link-modules.ts",
    "chars": 460,
    "preview": "import fs from 'fs';\nimport webpackPaths from '../configs/webpack.paths';\n\nconst { srcNodeModulesPath, appNodeModulesPat"
  },
  {
    "path": ".erb/scripts/notarize.js",
    "chars": 959,
    "preview": "const { notarize } = require('@electron/notarize');\nconst { build } = require('../../package.json');\n\nexports.default = "
  },
  {
    "path": ".eslintignore",
    "chars": 472,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Coverage directory used by tools like istanbul\ncoverage\n.eslintca"
  },
  {
    "path": ".eslintrc.js",
    "chars": 2437,
    "preview": "module.exports = {\n    extends: 'erb',\n    plugins: ['@typescript-eslint'],\n    rules: {\n        // A temporary hack rel"
  },
  {
    "path": ".gitattributes",
    "chars": 188,
    "preview": "*       text    eol=lf\n*.exe   binary\n*.png   binary\n*.jpg   binary\n*.jpeg  binary\n*.ico   binary\n*.icns  binary\n*.eot  "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "chars": 2278,
    "preview": "name: Bug Report\ndescription: Report a bug encountered while using Oblivion Desktop\nbody:\n    - type: markdown\n      att"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 574,
    "preview": "blank_issues_enabled: false\ncontact_links:\n    - name: Wiki\n      url: https://github.com/bepass-org/oblivion-desktop/wi"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "chars": 639,
    "preview": "name: Feature Request\ndescription: Request a new feature\nlabels: ['enhancement']\n\nbody:\n    - type: markdown\n      attri"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 310,
    "preview": "### Change Description\n\nWhat does this change add to or fix in the project?\n\n### Checklist\n\n- [ ] Code has been tested.\n"
  },
  {
    "path": ".github/config.yml",
    "chars": 131,
    "preview": "requiredHeaders:\n    - Prerequisites\n    - Expected Behavior\n    - Current Behavior\n    - Possible Solution\n    - Your E"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 239,
    "preview": "version: 2\nupdates:\n    - package-ecosystem: 'npm'\n      directory: '/'\n      schedule:\n          interval: 'monthly'\n  "
  },
  {
    "path": ".github/release_message.md",
    "chars": 6263,
    "preview": "## Pre-release RELEASE_TAG has been released for Windows, Linux & macOS.\n\n### Some software changes:\n\n- [x] Fixed some m"
  },
  {
    "path": ".github/stale.yml",
    "chars": 698,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a "
  },
  {
    "path": ".github/workflows/deploy-ppa.yml",
    "chars": 2613,
    "preview": "name: Build and Upload to Launchpad PPA\n\non:\n    workflow_dispatch: {}\n\njobs:\n    build-and-upload:\n        runs-on: ubu"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 3504,
    "preview": "name: Publish\n\npermissions: write-all\n\non:\n    push:\n        tags:\n            - '*'\n\njobs:\n    create-release:\n        "
  },
  {
    "path": ".gitignore",
    "chars": 534,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Coverage directory used by tools like istanbul\ncoverage\n.eslintca"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".husky/pre-push",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".prettierignore",
    "chars": 573,
    "preview": ".erb\n.vscode\nnode_modules\n.git\n\n# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Coverage directory used by tools "
  },
  {
    "path": ".prettierrc",
    "chars": 149,
    "preview": "{\n    \"tabWidth\": 4,\n    \"useTabs\": false,\n    \"singleQuote\": true,\n    \"jsxSingleQuote\": true,\n    \"printWidth\": 100,\n "
  },
  {
    "path": ".vscode/launch.json",
    "chars": 651,
    "preview": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Electron: Main\",\n      \"type\": \"node\",\n      \"request"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 1610,
    "preview": "{\n  \"files.associations\": {\n    \".eslintrc\": \"jsonc\",\n    \".prettierrc\": \"jsonc\",\n    \".eslintignore\": \"ignore\"\n  },\n  \n"
  },
  {
    "path": "@types/node-aplay.d.ts",
    "chars": 285,
    "preview": "declare module 'node-aplay' {\n    export default class Aplay {\n        constructor(file: string);\n\n        play(): void;"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 58,
    "preview": "<https://github.com/bepass-org/oblivion-desktop/releases>\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5220,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3554,
    "preview": "# CONTRIBUTION GUIDE\n\nThank you for your interest in contributing to Oblivion Desktop! We appreciate your help and suppo"
  },
  {
    "path": "DOCS.md",
    "chars": 3013,
    "preview": "# DEVELOPER DOCUMENTATION\n\nOblivion Desktop is an [Electron](https://www.electronjs.org/) project bootstrapped with [Ele"
  },
  {
    "path": "FAQ.md",
    "chars": 5168,
    "preview": "# سوالات متداول\n\n- [کاربرد اپ Oblivion Desktop چیه؟](#کاربرد-اپ-oblivion-desktop-چیه)\n- [این برنامه چطور کار می‌کنه؟](#ا"
  },
  {
    "path": "LICENSE.md",
    "chars": 1255,
    "preview": "# Restrictive License - No Sale and Modification Limitation\n\nCopyright © 2025 Oblivion Desktop\n\nThis software is provide"
  },
  {
    "path": "README-fa.md",
    "chars": 12433,
    "preview": "<div align=\"center\">\n    <h1>Oblivion Desktop</h1>\n</div>\n\n<div align=\"center\">\n    <p>\n        <a href=\"README.md\">\n   "
  },
  {
    "path": "README.md",
    "chars": 12819,
    "preview": "<div align=\"center\">\n    <h1>Oblivion Desktop</h1>\n</div>\n\n<div align=\"center\">\n    <p>\n        <a href=\"README-fa.md\">\n"
  },
  {
    "path": "SECURITY.md",
    "chars": 1263,
    "preview": "# امنیت برنامه\n\nاپ متن‌باز Oblivion به‌عنوان یکی‌از امن‌ترین و مطمئن‌ترین VPNها، در روزهای پراختلال اخیر نقش مهمی برای د"
  },
  {
    "path": "assets/assets.d.ts",
    "chars": 623,
    "preview": "type Styles = Record<string, string>;\n\ndeclare module '*.svg' {\n  import React = require('react');\n\n  export const React"
  },
  {
    "path": "assets/css/materialIcons.css",
    "chars": 1136,
    "preview": "@font-face {\n    font-family: 'Material Icons';\n    font-style: normal;\n    font-weight: 400;\n    src: url('../font/mate"
  },
  {
    "path": "assets/css/noto.css",
    "chars": 232,
    "preview": "@font-face {\n    font-family: 'Noto Color Emoji';\n    font-style: normal;\n    font-weight: 400;\n    font-display: swap;\n"
  },
  {
    "path": "assets/css/shabnam.css",
    "chars": 2073,
    "preview": "@font-face {\n    font-family: shabnam;\n    src: url('../font/shabnam/Shabnam-Thin.eot');\n    src:\n        url('../font/s"
  },
  {
    "path": "assets/css/style.css",
    "chars": 50376,
    "preview": "body {\n    font: 300 17px shabnam;\n    background: #fff;\n    color: #4c4b4b;\n    line-height: 20px;\n    word-wrap: break"
  },
  {
    "path": "assets/entitlements.mac.plist",
    "chars": 333,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "assets/json/1713988096625.json",
    "chars": 8604,
    "preview": "{\"nm\":\"Ella Shimmer\",\"ddd\":0,\"h\":300,\"w\":300,\"meta\":{\"g\":\"LottieFiles AE 3.0.2\"},\"layers\":[{\"ty\":3,\"nm\":\"Null 1\",\"sr\":1,"
  },
  {
    "path": "assets/proto/oblivion.proto",
    "chars": 553,
    "preview": "syntax = \"proto3\";\n\npackage oblivionHelper;\n\nservice OblivionService {\n  rpc Start (StartRequest) returns (StartResponse"
  },
  {
    "path": "package.json",
    "chars": 11787,
    "preview": "{\n    \"name\": \"oblivion-desktop\",\n    \"description\": \"unofficial desktop version of oblivion\",\n    \"shortName\": \"oblivio"
  },
  {
    "path": "release/app/package.json",
    "chars": 759,
    "preview": "{\n    \"name\": \"oblivion-desktop\",\n    \"description\": \"unofficial desktop version of oblivion\",\n    \"version\": \"3.11.0\",\n"
  },
  {
    "path": "script/beforePackHook.js",
    "chars": 534,
    "preview": "const util = require('util');\nconst exec = util.promisify(require('child_process').exec);\n\nexports.default = async funct"
  },
  {
    "path": "script/changeVersion.ts",
    "chars": 2782,
    "preview": "import fs from 'fs';\nimport path from 'path';\nimport { exec } from 'child_process';\n\nlet v = process.argv[2];\nif (v.char"
  },
  {
    "path": "script/dlBins.ts",
    "chars": 6909,
    "preview": "import fs from 'fs';\nimport axios from 'axios';\nimport decompress from 'decompress';\nimport { doesDirectoryExist, doesFi"
  },
  {
    "path": "script/makeRegeditVBSAvailable.ts",
    "chars": 646,
    "preview": "// https://www.npmjs.com/package/regedit#a-note-about-electron\n\nimport fs from 'fs';\n\n(async () => {\n    const vbsAssets"
  },
  {
    "path": "script/playground.ts",
    "chars": 155,
    "preview": "import { doesFileExist } from '../src/main/lib/utils';\n\n(async () => {\n    const tmp = await doesFileExist('bin');\n    c"
  },
  {
    "path": "script/postinstall.ts",
    "chars": 535,
    "preview": "import { exec } from 'child_process';\n\n(async () => {\n    if (process.platform === 'win32') {\n        exec('npm run post"
  },
  {
    "path": "src/__tests__/App.test.tsx",
    "chars": 235,
    "preview": "import '@testing-library/jest-dom';\nimport { render } from '@testing-library/react';\nimport App from '../renderer/App';\n"
  },
  {
    "path": "src/constants.ts",
    "chars": 5184,
    "preview": "import { app } from 'electron';\nimport path from 'path';\nimport SingBoxManager from './main/lib/sbManager';\nimport NetSt"
  },
  {
    "path": "src/defaultSettings.ts",
    "chars": 6896,
    "preview": "import { DropdownItem } from './renderer/components/Dropdown';\n\nexport type settingsKeys =\n    | 'scan'\n    | 'endpoint'"
  },
  {
    "path": "src/localization/am.ts",
    "chars": 11569,
    "preview": "import { Language } from './type';\n\nconst amharic: Language = {\n    global: {},\n    status: {\n        connecting: 'እንደምን"
  },
  {
    "path": "src/localization/ar.ts",
    "chars": 13030,
    "preview": "import { Language } from './type';\n\nconst arabic: Language = {\n    global: {},\n    status: {\n        connecting: 'جارٍ ا"
  },
  {
    "path": "src/localization/cn.ts",
    "chars": 9353,
    "preview": "import { Language } from './type';\n\nconst chinese: Language = {\n    global: {},\n    status: {\n        connecting: '连接中.."
  },
  {
    "path": "src/localization/electron.ts",
    "chars": 1010,
    "preview": "import { ipcRenderer } from 'electron';\n\nimport enUS from './en';\nimport faIR from './fa';\nimport ruRU from './ru';\nimpo"
  },
  {
    "path": "src/localization/en.ts",
    "chars": 13512,
    "preview": "import { Language } from './type';\n\nconst english: Language = {\n    global: {},\n    status: {\n        connecting: 'Conne"
  },
  {
    "path": "src/localization/es.ts",
    "chars": 15356,
    "preview": "import { Language } from './type';\n\nconst spanish: Language = {\n    global: {},\n    status: {\n        connecting: 'Conec"
  },
  {
    "path": "src/localization/fa.ts",
    "chars": 13312,
    "preview": "import { Language } from './type';\n\nconst persian: Language = {\n    global: {},\n    status: {\n        connecting: 'درحال"
  },
  {
    "path": "src/localization/id.ts",
    "chars": 14280,
    "preview": "import { Language } from './type';\n\nconst indonesia: Language = {\n    global: {},\n    status: {\n        connecting: 'Men"
  },
  {
    "path": "src/localization/index.ts",
    "chars": 1935,
    "preview": "import enUS from './en';\nimport faIR from './fa';\nimport ruRU from './ru';\nimport cnCN from './cn';\nimport trTR from './"
  },
  {
    "path": "src/localization/my.ts",
    "chars": 14603,
    "preview": "import { Language } from './type';\n\nconst myanmar: Language = {\n    global: {},\n    status: {\n        connecting: 'ဆက်သွ"
  },
  {
    "path": "src/localization/pt.ts",
    "chars": 14810,
    "preview": "import { Language } from './type';\n\nconst brazilianPortuguese: Language = {\n    global: {},\n    status: {\n        connec"
  },
  {
    "path": "src/localization/ru.ts",
    "chars": 14642,
    "preview": "import { Language } from './type';\n\nconst russian: Language = {\n    global: {},\n    status: {\n        connecting: 'Подкл"
  },
  {
    "path": "src/localization/tr.ts",
    "chars": 14137,
    "preview": "import { Language } from './type';\n\nconst turkish: Language = {\n    global: {},\n    status: {\n        connecting: 'Bağla"
  },
  {
    "path": "src/localization/type.ts",
    "chars": 7292,
    "preview": "export interface Global {}\n\nexport interface Status {\n    connecting: string;\n    connected: string;\n    connected_confi"
  },
  {
    "path": "src/localization/ur.ts",
    "chars": 14033,
    "preview": "import { Language } from './type';\n\nconst urdu: Language = {\n    global: {},\n    status: {\n        connecting: 'کنیکٹ ہو"
  },
  {
    "path": "src/localization/useTranslate.ts",
    "chars": 1522,
    "preview": "import { useState, useEffect } from 'react';\nimport { defaultSettings } from '../defaultSettings';\nimport enUS from './e"
  },
  {
    "path": "src/localization/vi.ts",
    "chars": 14072,
    "preview": "import { Language } from './type';\n\nconst vietnamese: Language = {\n    global: {},\n    status: {\n        connecting: 'Đa"
  },
  {
    "path": "src/main/config.ts",
    "chars": 189,
    "preview": "export const wpVersion = '1.2.6';\nexport const helperVersion = '1.3.2';\nexport const netStatsVersion = '1.0.2';\nexport c"
  },
  {
    "path": "src/main/dxConfig.ts",
    "chars": 351,
    "preview": "// for the sake of better developer experience 🧑‍💻\n// ! don't commit this file if you change it(git update-index --skip-"
  },
  {
    "path": "src/main/ipc.ts",
    "chars": 283,
    "preview": "// https://www.electronjs.org/docs/latest/tutorial/ipc\n\nimport { ipcMain } from 'electron';\nimport './lib/wpManager';\nim"
  },
  {
    "path": "src/main/ipcListeners/log.ts",
    "chars": 5367,
    "preview": "import fs from 'fs';\nimport os from 'os';\nimport { osInfo } from 'systeminformation';\nimport { app, ipcMain } from 'elec"
  },
  {
    "path": "src/main/ipcListeners/settings.ts",
    "chars": 491,
    "preview": "import { ipcMain } from 'electron';\nimport settings from 'electron-settings';\n\nipcMain.handle('settings', (event, mode, "
  },
  {
    "path": "src/main/lib/customEvent.ts",
    "chars": 308,
    "preview": "import EventEmitter from 'events';\n\nexport const customEvent = new EventEmitter();\n\n/*\n    EXAMPLE: 👇\n*/\n\n// listen for "
  },
  {
    "path": "src/main/lib/netStatsManager.ts",
    "chars": 3380,
    "preview": "import { ipcMain } from 'electron';\nimport log from 'electron-log';\nimport { ChildProcess, spawn } from 'child_process';"
  },
  {
    "path": "src/main/lib/pacScript.ts",
    "chars": 4783,
    "preview": "// proxy setup script\n\nimport handler from 'serve-handler';\nimport http from 'http';\nimport { app } from 'electron';\nimp"
  },
  {
    "path": "src/main/lib/proxy.ts",
    "chars": 24861,
    "preview": "import settings from 'electron-settings';\nimport { IpcMainEvent } from 'electron';\nimport log from 'electron-log';\nimpor"
  },
  {
    "path": "src/main/lib/sbConfig.ts",
    "chars": 20583,
    "preview": "import fs from 'fs';\nimport log from 'electron-log';\nimport path from 'path';\nimport {\n    sbConfigPath,\n    sbLogPath,\n"
  },
  {
    "path": "src/main/lib/sbHelper.ts",
    "chars": 3251,
    "preview": "// eslint-disable-next-line max-classes-per-file\nimport { isWindows, ICommand, IPlatformHelper, IRoutingRules } from '.."
  },
  {
    "path": "src/main/lib/sbManager.ts",
    "chars": 19083,
    "preview": "import { ipcMain, IpcMainEvent } from 'electron';\nimport log from 'electron-log';\nimport settings from 'electron-setting"
  },
  {
    "path": "src/main/lib/speedTestManager.ts",
    "chars": 2287,
    "preview": "import { IpcMainEvent, ipcMain } from 'electron';\nimport log from 'electron-log';\nimport SpeedTest, { MeasurementConfig "
  },
  {
    "path": "src/main/lib/utils.ts",
    "chars": 8866,
    "preview": "import fs from 'fs';\nimport { app, ipcMain } from 'electron';\nimport log from 'electron-log';\nimport { defaultSettings, "
  },
  {
    "path": "src/main/lib/wpHelper.ts",
    "chars": 5765,
    "preview": "import { IpcMainEvent } from 'electron';\nimport settings from 'electron-settings';\nimport { countries, defaultSettings }"
  },
  {
    "path": "src/main/lib/wpManager.ts",
    "chars": 17807,
    "preview": "import { app, ipcMain, BrowserWindow, dialog, shell } from 'electron';\nimport { spawn, ChildProcess, execSync, execFile "
  },
  {
    "path": "src/main/main.ts",
    "chars": 45214,
    "preview": "import {\n    app,\n    BrowserWindow,\n    ipcMain,\n    screen,\n    shell,\n    Menu,\n    Tray,\n    nativeImage,\n    IpcMai"
  },
  {
    "path": "src/main/menu.ts",
    "chars": 2091,
    "preview": "import { Menu, shell, BrowserWindow } from 'electron';\nimport { exitTheApp } from './lib/utils';\n//import { regeditVbsDi"
  },
  {
    "path": "src/main/playground.ts",
    "chars": 2159,
    "preview": "import { isDev } from './lib/utils';\n\nexport const devPlayground = () => {\n    if (!isDev()) return;\n    console.log('--"
  },
  {
    "path": "src/main/preload.ts",
    "chars": 2503,
    "preview": "import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';\n\nexport type Channels =\n    | 'ipc-example'\n   "
  },
  {
    "path": "src/renderer/App.tsx",
    "chars": 806,
    "preview": "import { MemoryRouter as Router } from 'react-router';\nimport { useEffect } from 'react';\n\nimport 'assets/css/bootstrap."
  },
  {
    "path": "src/renderer/components/BackButton.tsx",
    "chars": 274,
    "preview": "import { Link } from 'react-router';\nimport classNames from 'classnames';\n\nexport default function BackButton() {\n    re"
  },
  {
    "path": "src/renderer/components/Card/index.tsx",
    "chars": 382,
    "preview": "import { FC, memo } from 'react';\n\ninterface ResultCardProps {\n    label: string;\n    value: string;\n    unit: string;\n}"
  },
  {
    "path": "src/renderer/components/ConfigHandler.tsx",
    "chars": 1072,
    "preview": "import { FC, useEffect } from 'react';\nimport { saveConfig } from '../lib/inputSanitizer';\nimport { Language } from '../"
  },
  {
    "path": "src/renderer/components/Dropdown/index.tsx",
    "chars": 1340,
    "preview": "import { ChangeEvent, FC } from 'react';\n\nexport type DropdownItem = {\n    label: string;\n    value: string;\n};\n\ninterfa"
  },
  {
    "path": "src/renderer/components/Input/index.tsx",
    "chars": 1952,
    "preview": "import React, { ChangeEvent, FC } from 'react';\nimport useInput from './useInput';\n\ninterface InputProps {\n    id?: stri"
  },
  {
    "path": "src/renderer/components/Input/useInput.ts",
    "chars": 3821,
    "preview": "import React, { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react';\n\ntype ContextMenuStyleType = {\n   "
  },
  {
    "path": "src/renderer/components/Modal/DNS/index.tsx",
    "chars": 5043,
    "preview": "import classNames from 'classnames';\nimport useDnsModal from './useDnsModal';\nimport Input from '../../Input';\n\ninterfac"
  },
  {
    "path": "src/renderer/components/Modal/DNS/useDnsModal.ts",
    "chars": 2998,
    "preview": "import { ChangeEvent, useCallback, useEffect, useState } from 'react';\nimport useTranslate from '../../../../localizatio"
  },
  {
    "path": "src/renderer/components/Modal/Endpoint/index.tsx",
    "chars": 11261,
    "preview": "import classNames from 'classnames';\nimport { FC } from 'react';\nimport { defaultSettings } from '../../../../defaultSet"
  },
  {
    "path": "src/renderer/components/Modal/Endpoint/useEndpointModal.ts",
    "chars": 8462,
    "preview": "import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useStore } from '../../"
  },
  {
    "path": "src/renderer/components/Modal/License/index.tsx",
    "chars": 3261,
    "preview": "import classNames from 'classnames';\nimport useLicenseModal from './useLicenseModal';\nimport Input from '../../Input';\n\n"
  },
  {
    "path": "src/renderer/components/Modal/License/useLicenseModal.ts",
    "chars": 2628,
    "preview": "import { ChangeEvent, useCallback, useEffect, useState } from 'react';\nimport { settings } from '../../../lib/settings';"
  },
  {
    "path": "src/renderer/components/Modal/MTU/index.tsx",
    "chars": 2580,
    "preview": "import classNames from 'classnames';\nimport { defaultSettings } from '../../../../defaultSettings';\nimport useMTUModal f"
  },
  {
    "path": "src/renderer/components/Modal/MTU/useMTUModal.ts",
    "chars": 2442,
    "preview": "import { ChangeEvent, useCallback, useEffect, useState } from 'react';\n//import { settings } from '../../../lib/settings"
  },
  {
    "path": "src/renderer/components/Modal/Port/index.tsx",
    "chars": 2464,
    "preview": "import classNames from 'classnames';\nimport { defaultSettings } from '../../../../defaultSettings';\nimport usePortModal "
  },
  {
    "path": "src/renderer/components/Modal/Port/usePortModal.ts",
    "chars": 2555,
    "preview": "import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';\nimport { settings } from '../../.."
  },
  {
    "path": "src/renderer/components/Modal/Profile/index.tsx",
    "chars": 7243,
    "preview": "import { FC } from 'react';\nimport classNames from 'classnames';\nimport useProfileModal from './useProfileModal';\nimport"
  },
  {
    "path": "src/renderer/components/Modal/Profile/useProfileModal.ts",
    "chars": 5298,
    "preview": "import { useCallback, useEffect, useState } from 'react';\nimport toast from 'react-hot-toast';\nimport { settings } from "
  },
  {
    "path": "src/renderer/components/Modal/Restore/index.tsx",
    "chars": 2466,
    "preview": "import classNames from 'classnames';\nimport useRestoreModal from './useRestoreModal';\n\ninterface RestoreModalProps {\n   "
  },
  {
    "path": "src/renderer/components/Modal/Restore/useRestoreModal.ts",
    "chars": 6542,
    "preview": "import { useCallback, useEffect, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n"
  },
  {
    "path": "src/renderer/components/Modal/RoutingRules/index.tsx",
    "chars": 4372,
    "preview": "import classNames from 'classnames';\nimport useRoutingRulesModal from './useRoutingRulesModal';\nimport Textarea from '.."
  },
  {
    "path": "src/renderer/components/Modal/RoutingRules/useRoutingRulesModal.ts",
    "chars": 4844,
    "preview": "import { ChangeEvent, useCallback, useEffect, useState } from 'react';\nimport { settings } from '../../../lib/settings';"
  },
  {
    "path": "src/renderer/components/Modal/TestUrl/index.tsx",
    "chars": 6891,
    "preview": "import classNames from 'classnames';\nimport useTestUrlModal from './useTestUrlModal';\nimport Input from '../../Input';\ni"
  },
  {
    "path": "src/renderer/components/Modal/TestUrl/useTestUrlModal.ts",
    "chars": 4576,
    "preview": "import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { settings } from '../../"
  },
  {
    "path": "src/renderer/components/Nav/index.tsx",
    "chars": 505,
    "preview": "import { FC } from 'react';\nimport BackButton from '../BackButton';\nimport useNav from './useNav';\n\ninterface NavProps {"
  },
  {
    "path": "src/renderer/components/Nav/useNav.ts",
    "chars": 759,
    "preview": "import { useCallback, useEffect } from 'react';\n//import { ipcRenderer } from '../../lib/utils';\n\nconst useNav = () => {"
  },
  {
    "path": "src/renderer/components/Tabs.tsx",
    "chars": 2625,
    "preview": "import classNames from 'classnames';\nimport { Link } from 'react-router';\nimport useTranslate from '../../localization/u"
  },
  {
    "path": "src/renderer/components/Textarea/index.tsx",
    "chars": 1802,
    "preview": "import React, { ChangeEvent, FC } from 'react';\nimport useTextarea from './useTextarea';\n\ninterface InputProps {\n    id?"
  },
  {
    "path": "src/renderer/components/Textarea/useTextarea.ts",
    "chars": 3711,
    "preview": "import React, { ChangeEvent, useState, useEffect, useRef, useCallback } from 'react';\n\ntype ContextMenuStyleType = {\n   "
  },
  {
    "path": "src/renderer/context/GlobalContext.tsx",
    "chars": 379,
    "preview": "import React from 'react';\nimport { openDevtoolsOnCtrlShiftI } from '../lib/dx';\nimport useGoBackOnEscape from '../hooks"
  },
  {
    "path": "src/renderer/hooks/useButtonKeyDown.ts",
    "chars": 354,
    "preview": "import { KeyboardEvent, useCallback } from 'react';\n\nconst useButtonKeyDown = (func: () => void) => {\n    return useCall"
  },
  {
    "path": "src/renderer/hooks/useGoBackOnEscape.tsx",
    "chars": 471,
    "preview": "import { useEffect } from 'react';\nimport { useNavigate } from 'react-router';\nimport { onEscapeKeyPressed } from '../li"
  },
  {
    "path": "src/renderer/index.ejs",
    "chars": 276,
    "preview": "<!doctype html>\n<html lang=\"fa\" dir=\"rtl\" data-bs-theme=\"light\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta\n      h"
  },
  {
    "path": "src/renderer/index.tsx",
    "chars": 895,
    "preview": "import { createRoot } from 'react-dom/client';\nimport App from './App';\nimport { ipcRenderer } from './lib/utils';\n\ncons"
  },
  {
    "path": "src/renderer/lib/cfFlag.ts",
    "chars": 379,
    "preview": "export const cfFlag = (code: string) => {\n    const flag = code?.trim().toUpperCase();\n    try {\n        if (flag === 'I"
  },
  {
    "path": "src/renderer/lib/dx.ts",
    "chars": 689,
    "preview": "// improves developer experience\n\nimport { useEffect } from 'react';\nimport { ipcRenderer } from './utils';\n\nexport cons"
  },
  {
    "path": "src/renderer/lib/getIspName.ts",
    "chars": 2217,
    "preview": "import { settings } from './settings';\n\nconst getAsnCode = (name: string) => {\n    switch (true) {\n        case name?.in"
  },
  {
    "path": "src/renderer/lib/globalEvents.ts",
    "chars": 5723,
    "preview": "import { useEffect } from 'react';\nimport { ipcRenderer } from './utils';\nimport { useStore } from '../store';\nimport {\n"
  },
  {
    "path": "src/renderer/lib/inputSanitizer.ts",
    "chars": 10181,
    "preview": "import toast from 'react-hot-toast';\nimport { settings } from './settings';\nimport { countries, defaultSettings } from '"
  },
  {
    "path": "src/renderer/lib/isAnyUndefined.ts",
    "chars": 340,
    "preview": "export function isAnyUndefined(...values: unknown[]): boolean {\n    return values.some((value) => typeof value === 'unde"
  },
  {
    "path": "src/renderer/lib/loaders.ts",
    "chars": 2195,
    "preview": "import { getDirectionByLang } from '../../localization';\nimport { defaultSettings } from '../../defaultSettings';\nimport"
  },
  {
    "path": "src/renderer/lib/settings.ts",
    "chars": 1317,
    "preview": "import { settingsKeys } from '../../defaultSettings';\nimport { ipcRenderer } from './utils';\n\nexport class settings {\n  "
  },
  {
    "path": "src/renderer/lib/systemDateValidator.ts",
    "chars": 228,
    "preview": "export const isSystemDateValid = (): boolean => {\n    const now = new Date();\n    const currentYear = now.getFullYear();"
  },
  {
    "path": "src/renderer/lib/toPersianNumber.ts",
    "chars": 466,
    "preview": "import { typeIsUndefined } from './isAnyUndefined';\n\nexport const farsiDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷',"
  },
  {
    "path": "src/renderer/lib/toasts.tsx",
    "chars": 2658,
    "preview": "import toast from 'react-hot-toast';\nimport { Language } from '../../localization/type';\n\nconst defaultToastStyle = {\n  "
  },
  {
    "path": "src/renderer/lib/utils.ts",
    "chars": 425,
    "preview": "export const { ipcRenderer, platform, NODE_ENV, username, arch } = window.electron;\n\nexport const isDev = () => window.e"
  },
  {
    "path": "src/renderer/lib/withDefault.ts",
    "chars": 125,
    "preview": "export const withDefault = <T>(value: T | undefined, fallback: T): T =>\n    typeof value === 'undefined' ? fallback : va"
  },
  {
    "path": "src/renderer/pages/About/index.tsx",
    "chars": 6254,
    "preview": "import classNames from 'classnames';\nimport Nav from '../../components/Nav';\nimport packageJsonData from '../../../../pa"
  },
  {
    "path": "src/renderer/pages/Debug/index.tsx",
    "chars": 2763,
    "preview": "import { Toaster } from 'react-hot-toast';\nimport classNames from 'classnames';\nimport Nav from '../../components/Nav';\n"
  },
  {
    "path": "src/renderer/pages/Debug/useDebug.ts",
    "chars": 2875,
    "preview": "import {\n    KeyboardEvent,\n    MouseEvent,\n    useCallback,\n    useEffect,\n    useMemo,\n    useRef,\n    useState\n} from"
  },
  {
    "path": "src/renderer/pages/Landing/DownloadProgressBar.tsx",
    "chars": 974,
    "preview": "import classNames from 'classnames';\nimport { useState, useEffect, FC } from 'react';\n\ntype DownloadProgress = {\n    per"
  },
  {
    "path": "src/renderer/pages/Landing/LandingBody.tsx",
    "chars": 8788,
    "preview": "import classNames from 'classnames';\nimport { FC, FormEvent } from 'react';\n//import { Swipe } from 'react-swipe-compone"
  },
  {
    "path": "src/renderer/pages/Landing/LandingDrawer.tsx",
    "chars": 6348,
    "preview": "import { FC } from 'react';\nimport { Link } from 'react-router';\n\nimport Drawer from 'react-modern-drawer';\nimport appIc"
  },
  {
    "path": "src/renderer/pages/Landing/LandingHeader.tsx",
    "chars": 2945,
    "preview": "import { FC, KeyboardEvent, useEffect, useState } from 'react';\nimport classNames from 'classnames';\nimport { Link } fro"
  },
  {
    "path": "src/renderer/pages/Landing/index.tsx",
    "chars": 3325,
    "preview": "//import { useState } from 'react';\nimport { Toaster } from 'react-hot-toast';\nimport 'react-modern-drawer/dist/index.cs"
  },
  {
    "path": "src/renderer/pages/Landing/useLanding.ts",
    "chars": 14363,
    "preview": "import { FormEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';\nimport toast from 'react-hot-toast';"
  },
  {
    "path": "src/renderer/pages/Network/index.tsx",
    "chars": 10755,
    "preview": "import classNames from 'classnames';\nimport { Toaster } from 'react-hot-toast';\nimport useOptions from './useOptions';\ni"
  },
  {
    "path": "src/renderer/pages/Network/useOptions.ts",
    "chars": 11605,
    "preview": "import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react';\nimport { useStore } from '../../store';\n"
  },
  {
    "path": "src/renderer/pages/Options/index.tsx",
    "chars": 13542,
    "preview": "import classNames from 'classnames';\nimport { Toaster } from 'react-hot-toast';\nimport Nav from '../../components/Nav';\n"
  },
  {
    "path": "src/renderer/pages/Options/useOptions.ts",
    "chars": 7565,
    "preview": "import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { useLocation } from 'react-router"
  },
  {
    "path": "src/renderer/pages/Scanner/index.tsx",
    "chars": 7078,
    "preview": "import classNames from 'classnames';\nimport { Toaster } from 'react-hot-toast';\nimport Nav from '../../components/Nav';\n"
  },
  {
    "path": "src/renderer/pages/Scanner/useScanner.ts",
    "chars": 6278,
    "preview": "import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react';\nimport { useStore } from '../../store';\n"
  },
  {
    "path": "src/renderer/pages/Settings/index.tsx",
    "chars": 9624,
    "preview": "import classNames from 'classnames';\nimport { Toaster } from 'react-hot-toast';\nimport Nav from '../../components/Nav';\n"
  },
  {
    "path": "src/renderer/pages/Settings/useSettings.ts",
    "chars": 5571,
    "preview": "import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react';\nimport { useStore } from '../../store';\n"
  },
  {
    "path": "src/renderer/pages/SingBox/index.tsx",
    "chars": 17997,
    "preview": "import classNames from 'classnames';\nimport { Toaster } from 'react-hot-toast';\nimport Nav from '../../components/Nav';\n"
  },
  {
    "path": "src/renderer/pages/SingBox/useSingBox.ts",
    "chars": 4350,
    "preview": "import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';\nimport { settings } from '../../li"
  },
  {
    "path": "src/renderer/pages/SpeedTest/index.tsx",
    "chars": 3247,
    "preview": "import { FC, useMemo } from 'react';\nimport classNames from 'classnames';\nimport { Toaster } from 'react-hot-toast';\nimp"
  },
  {
    "path": "src/renderer/pages/SpeedTest/useSpeedTest.ts",
    "chars": 3821,
    "preview": "import { useState, useEffect, useCallback, useMemo } from 'react';\nimport { ipcRenderer } from '../../lib/utils';\nimport"
  },
  {
    "path": "src/renderer/pages/SplashScreen/index.tsx",
    "chars": 1549,
    "preview": "import useSplashScreen from './useSplashScreen';\n\nexport default function SplashScreen() {\n    const { show } = useSplas"
  },
  {
    "path": "src/renderer/pages/SplashScreen/useSplashScreen.ts",
    "chars": 404,
    "preview": "import { useEffect, useState } from 'react';\nimport { isDev } from '../../lib/utils';\n\nconst useSplashScreen = () => {\n "
  },
  {
    "path": "src/renderer/preload.d.ts",
    "chars": 194,
    "preview": "import { ElectronHandler } from '../main/preload';\n\ndeclare global {\n    // eslint-disable-next-line no-unused-vars\n    "
  },
  {
    "path": "src/renderer/routes/index.tsx",
    "chars": 1189,
    "preview": "import { Route, Routes } from 'react-router';\nimport Landing from '../pages/Landing';\nimport Settings from '../pages/Set"
  },
  {
    "path": "src/renderer/store.ts",
    "chars": 1888,
    "preview": "import { create } from 'zustand';\nimport { getTranslate } from '../localization';\nimport { defaultSettings } from '../de"
  },
  {
    "path": "src/types/global.d.ts",
    "chars": 133,
    "preview": "export {};\n\ndeclare global {\n    interface Window {\n        platformAPI: {\n            getPlatform: () => string;\n      "
  },
  {
    "path": "tsconfig.json",
    "chars": 513,
    "preview": "{\n    \"compilerOptions\": {\n        \"incremental\": true,\n        \"target\": \"es2022\",\n        \"module\": \"commonjs\",\n      "
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the bepass-org/oblivion-desktop GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 184 files (826.2 KB), approximately 208.6k tokens, and a symbol index with 274 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!