diff --git a/client/desktop/.erb/configs/utils.ts b/client/desktop/.erb/configs/utils.ts deleted file mode 100644 index 0ed26521..00000000 --- a/client/desktop/.erb/configs/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import webWebpackConfig from '../../../web/build/webpack.config'; -import type { Configuration } from 'webpack'; -import webpackPaths from './webpack.paths'; - -export function getWebTailchatWebpackConfig(): Configuration { - return { - ...webWebpackConfig, - plugins: webWebpackConfig.plugins?.filter( - (p) => !['GenerateSW'].includes(p.constructor.name) - ), - output: { - path: webpackPaths.distRendererPath, - filename: '[name].[contenthash].js', - publicPath: '/', - }, - target: ['web', 'electron-renderer'], - }; -} diff --git a/client/desktop/.erb/configs/webpack.config.base.ts b/client/desktop/.erb/configs/webpack.config.base.ts index a5b60d44..0ef00445 100644 --- a/client/desktop/.erb/configs/webpack.config.base.ts +++ b/client/desktop/.erb/configs/webpack.config.base.ts @@ -3,11 +3,12 @@ */ 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 || {}), 'electron-screenshots'], + externals: [...Object.keys(externals || {})], stats: 'errors-only', @@ -21,6 +22,9 @@ const configuration: webpack.Configuration = { options: { // Remove this line to enable type checking in webpack builds transpileOnly: true, + compilerOptions: { + module: 'esnext', + }, }, }, }, @@ -41,22 +45,11 @@ const configuration: webpack.Configuration = { resolve: { extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], modules: [webpackPaths.srcPath, 'node_modules'], - fallback: { - crypto: require.resolve('crypto-browserify'), - http: require.resolve('stream-http'), - https: require.resolve('https-browserify'), - path: require.resolve('path-browserify'), - stream: require.resolve('stream-browserify'), - url: require.resolve('url/'), - fs: false, - }, + // There is no need to add aliases here, the paths in tsconfig get mirrored + plugins: [new TsconfigPathsPlugins()], }, plugins: [ - new webpack.DefinePlugin({ - 'process.env.FE_URL': JSON.stringify(process.env.FE_URL), - 'process.env.SERVICE_URL': JSON.stringify(process.env.SERVICE_URL), - }), new webpack.EnvironmentPlugin({ NODE_ENV: 'production', }), diff --git a/client/desktop/.erb/configs/webpack.config.eslint.ts b/client/desktop/.erb/configs/webpack.config.eslint.ts deleted file mode 100644 index 35a631b7..00000000 --- a/client/desktop/.erb/configs/webpack.config.eslint.ts +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint import/no-unresolved: off, import/no-self-import: off */ - -module.exports = require('./webpack.config.renderer.dev').default; diff --git a/client/desktop/.erb/configs/webpack.config.main.prod.ts b/client/desktop/.erb/configs/webpack.config.main.prod.ts index 3ed351f1..47274821 100644 --- a/client/desktop/.erb/configs/webpack.config.main.prod.ts +++ b/client/desktop/.erb/configs/webpack.config.main.prod.ts @@ -15,15 +15,8 @@ import deleteSourceMaps from '../scripts/delete-source-maps'; checkNodeEnv('production'); deleteSourceMaps(); -const devtoolsConfig = - process.env.DEBUG_PROD === 'true' - ? { - devtool: 'source-map', - } - : {}; - const configuration: webpack.Configuration = { - ...devtoolsConfig, + devtool: 'source-map', mode: 'production', @@ -37,6 +30,9 @@ const configuration: webpack.Configuration = { output: { path: webpackPaths.distMainPath, filename: '[name].js', + library: { + type: 'umd', + }, }, optimization: { @@ -50,6 +46,7 @@ const configuration: webpack.Configuration = { plugins: [ new BundleAnalyzerPlugin({ analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', + analyzerPort: 8888, }), /** @@ -66,6 +63,10 @@ const configuration: webpack.Configuration = { DEBUG_PROD: false, START_MINIMIZED: false, }), + + new webpack.DefinePlugin({ + 'process.type': '"browser"', + }), ], /** diff --git a/client/desktop/.erb/configs/webpack.config.preload.dev.ts b/client/desktop/.erb/configs/webpack.config.preload.dev.ts index 061db530..d6679e63 100644 --- a/client/desktop/.erb/configs/webpack.config.preload.dev.ts +++ b/client/desktop/.erb/configs/webpack.config.preload.dev.ts @@ -24,6 +24,9 @@ const configuration: webpack.Configuration = { output: { path: webpackPaths.dllPath, filename: 'preload.js', + library: { + type: 'umd', + }, }, plugins: [ diff --git a/client/desktop/.erb/configs/webpack.config.renderer.dev.dll.ts b/client/desktop/.erb/configs/webpack.config.renderer.dev.dll.ts new file mode 100644 index 00000000..614b90f0 --- /dev/null +++ b/client/desktop/.erb/configs/webpack.config.renderer.dev.dll.ts @@ -0,0 +1,77 @@ +/** + * 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); diff --git a/client/desktop/.erb/configs/webpack.config.renderer.dev.ts b/client/desktop/.erb/configs/webpack.config.renderer.dev.ts index 9677ab63..28a62fef 100644 --- a/client/desktop/.erb/configs/webpack.config.renderer.dev.ts +++ b/client/desktop/.erb/configs/webpack.config.renderer.dev.ts @@ -1,21 +1,174 @@ import 'webpack-dev-server'; -import type { Configuration } from 'webpack'; -import { spawn } from 'child_process'; -import { getWebTailchatWebpackConfig } from './utils'; +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'; -require('dotenv').config(); +// 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'); -const webWebpackConfig = getWebTailchatWebpackConfig(); +/** + * 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'], -const configuration: Configuration = { - ...webWebpackConfig, entry: [ `webpack-dev-server/client?http://localhost:${port}/dist`, 'webpack/hot/only-dev-server', - webWebpackConfig.entry?.['app'], + 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, @@ -37,7 +190,13 @@ const configuration: Configuration = { .on('error', (spawnError) => console.error(spawnError)); console.log('Starting Main Process...'); - spawn('npm', ['run', 'start:main'], { + 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', }) @@ -51,4 +210,4 @@ const configuration: Configuration = { }, }; -export default configuration; +export default merge(baseConfig, configuration); diff --git a/client/desktop/.erb/configs/webpack.config.renderer.prod.ts b/client/desktop/.erb/configs/webpack.config.renderer.prod.ts index c50da685..3cebf30d 100644 --- a/client/desktop/.erb/configs/webpack.config.renderer.prod.ts +++ b/client/desktop/.erb/configs/webpack.config.renderer.prod.ts @@ -1,11 +1,141 @@ -import type { Configuration } from 'webpack'; -import { getWebTailchatWebpackConfig } from './utils'; +/** + * Build config for electron renderer process + */ -const webWebpackConfig = getWebTailchatWebpackConfig(); +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', -const configuration: Configuration = { - ...webWebpackConfig, 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 configuration; +export default merge(baseConfig, configuration); diff --git a/client/desktop/.erb/configs/webpack.paths.ts b/client/desktop/.erb/configs/webpack.paths.ts index 7863defd..e5ba5734 100644 --- a/client/desktop/.erb/configs/webpack.paths.ts +++ b/client/desktop/.erb/configs/webpack.paths.ts @@ -7,7 +7,6 @@ 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 srcPublicPath = path.join(rootPath, '../app/public'); const releasePath = path.join(rootPath, 'release'); const appPath = path.join(releasePath, 'app'); @@ -18,7 +17,6 @@ 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 distPublicPath = path.join(distRendererPath, 'public'); const buildPath = path.join(releasePath, 'build'); @@ -37,6 +35,4 @@ export default { distMainPath, distRendererPath, buildPath, - srcPublicPath, - distPublicPath, }; diff --git a/client/desktop/package.json b/client/desktop/package.json index 617bb3c5..4904cdd2 100644 --- a/client/desktop/package.json +++ b/client/desktop/package.json @@ -38,14 +38,13 @@ "build": "concurrently \"yarn build:main\" \"yarn build:renderer\"", "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", - "postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps", + "postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts", "lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx", "package": "cross-env DEBUG_PROD=true yarn package:nodebug --config.asar=false", "package:all": "yarn package:nodebug -mwl", "package:nodebug": "ts-node ./.erb/scripts/clean.js dist && yarn build && electron-builder build --publish never", "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app", - "dev": "cross-env FE_URL=http://127.0.0.1:11011 SERVICE_URL=http://127.0.0.1:11000 yarn start", - "dev:nightly": "cross-env FE_URL=http://nightly.paw.msgbyte.com SERVICE_URL=https://tailchat-nightly.moonrailgun.com/ yarn start", + "dev": "yarn start", "start": "ts-node ./.erb/scripts/check-port-in-use.js && yarn start:renderer", "start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only .", "start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts", @@ -117,7 +116,6 @@ "electron-debug": "^3.2.0", "electron-is": "^3.0.0", "electron-log": "^4.4.7", - "electron-screenshots": "^0.5.15", "electron-serve": "^1.1.0", "electron-updater": "^5.0.1", "electron-window-state": "^5.0.3", @@ -126,7 +124,6 @@ "lodash": "^4.17.21", "react": "^18.1.0", "react-dom": "^18.1.0", - "react-router-dom": "^6.3.0", "source-ref-loader": "^1.0.7" }, "devDependencies": { @@ -205,6 +202,7 @@ "ts-jest": "27.1.4", "ts-loader": "^9.3.0", "ts-node": "^10.7.0", + "tsconfig-paths-webpack-plugin": "^4.0.1", "typescript": "^4.6.4", "url": "^0.11.0", "url-loader": "^4.1.1", diff --git a/client/desktop/release/app/package.json b/client/desktop/release/app/package.json index 5e5b4b92..b42f5eb9 100644 --- a/client/desktop/release/app/package.json +++ b/client/desktop/release/app/package.json @@ -14,5 +14,7 @@ "postinstall": "npm run electron-rebuild && npm run link-modules", "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts" }, - "dependencies": {} + "dependencies": { + "electron-screenshots": "^0.5.23" + } } diff --git a/client/desktop/release/app/yarn.lock b/client/desktop/release/app/yarn.lock index fb57ccd1..eda6c748 100644 --- a/client/desktop/release/app/yarn.lock +++ b/client/desktop/release/app/yarn.lock @@ -2,3 +2,111 @@ # yarn lockfile v1 +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +electron-screenshots@^0.5.23: + version "0.5.23" + resolved "https://registry.npmmirror.com/electron-screenshots/-/electron-screenshots-0.5.23.tgz#4d0b8ff8dee31033faf8b8680f3ff93d88d5df47" + integrity sha512-mcBDauRdKyuUOPX64inJwbBsbmFbmszDB0qlcc5ofO1q/SugiAvagVrajcK9jl9DWJwO4tVFDgqXJDHG6U//yA== + dependencies: + debug "^4.3.4" + fs-extra "^11.1.1" + node-screenshots "^0.1.4" + react-screenshots "^0.5.22" + +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +node-screenshots-darwin-arm64@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-darwin-arm64/-/node-screenshots-darwin-arm64-0.1.4.tgz#434e181156c235b7e41682de164732d2fbd8499c" + integrity sha512-PN9hy1Dd+vvRU6sol/sNBu1+XZ0VoUkZ1nWClmSjwLlwGYe2L5xNBnG1OTiiZeNCueBlyf/0k0vI5hognH/LVA== + +node-screenshots-darwin-universal@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-darwin-universal/-/node-screenshots-darwin-universal-0.1.4.tgz#93a9ce4b707f2e15114b75c67c89da7a030caa35" + integrity sha512-xYZrTyVI+8gWXr3mmhcvqUyv8lwWLruhNgE8ClhX+1y6rP8LKoZ5QohBuTPEtIo0oC0y0EETbBnHW/5IkRlijg== + +node-screenshots-darwin-x64@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-darwin-x64/-/node-screenshots-darwin-x64-0.1.4.tgz#5cff721d174d0b3b2f430c331012ad27ca852284" + integrity sha512-JDWSC7pmph7RN7SnJeXwwzKHk1anMk3cqskgT47h1t4Hu3tN1a0sKfKagglRaI9Y2IoDfBTLSZxNNwyCZag5iQ== + +node-screenshots-linux-x64-gnu@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-linux-x64-gnu/-/node-screenshots-linux-x64-gnu-0.1.4.tgz#77cc54ddfff941f34db61ad0aba0e3603e2e2937" + integrity sha512-oj39sWl9Xe8wGmHeUoAwLSN+0186XJrmVojOSEovBLCY2teUSHNPOq8D52z67mPv7S4bNHilj5b52jz9nl31/Q== + +node-screenshots-linux-x64-musl@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-linux-x64-musl/-/node-screenshots-linux-x64-musl-0.1.4.tgz#cc558db789d44a8fda7274b82f34e2ffb77c8b83" + integrity sha512-eHRC6BobZPRbyj01VIiaK84kg4plSGBSPYYSo4ObFzrFmD3dRaUgTfsACoi8th0XiNd25w6/FyAIKKGU5M8IPg== + +node-screenshots-win32-arm64-msvc@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-win32-arm64-msvc/-/node-screenshots-win32-arm64-msvc-0.1.4.tgz#a61c6d1f86761b24fdd379b8cd6249e2a2bda056" + integrity sha512-BgwLbAQwvnv+T0W47Dq9Pd7/IKTMRCie3ECW6aLG3jqsPhKMqXac8lyrJbaYR88lxlKWraWxREkb1sx6gQaUlA== + +node-screenshots-win32-ia32-msvc@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-win32-ia32-msvc/-/node-screenshots-win32-ia32-msvc-0.1.4.tgz#17809fd941fc822dcdffaa9312f95782f4f275b1" + integrity sha512-afZqWWbfZ3nChI8eeZ93J7bn5096Zq+NRutvqNZhBYLAGZd+hCWqcDumTgJ74oUMVi49Cn06isXxj+MB3RtUDQ== + +node-screenshots-win32-x64-msvc@0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots-win32-x64-msvc/-/node-screenshots-win32-x64-msvc-0.1.4.tgz#325df877896dd0533e63c90f0d8436e2c11a43e5" + integrity sha512-08mfx/6veDKrdx0KDVMqnYjZq0U4LIbezIKBIgI06Bj+S8CrgXBvTQTEu+lsL/PZMK+J9yimI2Bp77h2AqhaiQ== + +node-screenshots@^0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/node-screenshots/-/node-screenshots-0.1.4.tgz#ec6c7626aaea9afc2bd78ad379dd9feeb13cd6c8" + integrity sha512-QmbgiI0VkV3wu+Dn7Kn107/VnujdFgT3juDdaRv2GyOviVghP3+QD46IVU/IlXmzgxVFTjIiVBRrI6yovDnXiQ== + optionalDependencies: + node-screenshots-darwin-arm64 "0.1.4" + node-screenshots-darwin-universal "0.1.4" + node-screenshots-darwin-x64 "0.1.4" + node-screenshots-linux-x64-gnu "0.1.4" + node-screenshots-linux-x64-musl "0.1.4" + node-screenshots-win32-arm64-msvc "0.1.4" + node-screenshots-win32-ia32-msvc "0.1.4" + node-screenshots-win32-x64-msvc "0.1.4" + +react-screenshots@^0.5.22: + version "0.5.22" + resolved "https://registry.npmmirror.com/react-screenshots/-/react-screenshots-0.5.22.tgz#2d974189d2d2df74388a4a19482fd1c956641837" + integrity sha512-aooIlb1k6161BTqhhyADlMrlF0n1UdudjuhyvXaCQfwURXZNuUerMxFuOfwNCgSbPAHlxdHCWtYQ6iQT+RaF+w== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== diff --git a/client/desktop/src/main/main.ts b/client/desktop/src/main/main.ts index 33c1bf1e..8f0705e6 100644 --- a/client/desktop/src/main/main.ts +++ b/client/desktop/src/main/main.ts @@ -13,7 +13,7 @@ import { app, BrowserWindow, shell, ipcMain } from 'electron'; import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; import MenuBuilder from './menu'; -import { getMainWindowUrl } from './util'; +import { resolveHtmlPath } from './util'; import windowStateKeeper from 'electron-window-state'; import is from 'electron-is'; import { initScreenshots } from './screenshots'; @@ -73,8 +73,54 @@ const installExtensions = async () => { .catch(console.log); }; +const webPreferences: Electron.WebPreferences = { + nodeIntegration: false, + preload: app.isPackaged + ? path.join(__dirname, 'preload.js') + : path.join(__dirname, '../../.erb/dll/preload.js'), +}; + +let welcomeWindow: BrowserWindow | null = null; let mainWindow: BrowserWindow | null = null; -const createWindow = async () => { + +const createWelcomeWindow = async () => { + // 创建一个新的浏览器窗口 + welcomeWindow = new BrowserWindow({ + width: 800, + height: 600, + frame: false, + webPreferences, + }); + + // 加载欢迎窗口的HTML文件 + welcomeWindow.loadURL(resolveHtmlPath('index.html')); + + // 监听从渲染进程发送的选择用户事件 + // welcomeWindow.webContents.on('selectUser', (event, user) => { + // selectedUser = user; + // welcomeWindow.close(); // 关闭欢迎窗口 + // createMainWindow(); // 创建主窗口 + // }); + + welcomeWindow.webContents.on('ipc-message', (e, channel, data) => { + if (channel === 'close') { + welcomeWindow?.close(); + } else if (channel === 'selectServer') { + console.log(data); + const url = data.url; + + createMainWindow(url).then(() => { + welcomeWindow?.close(); + }); + } + }); + + welcomeWindow.on('closed', () => { + welcomeWindow = null; + }); +}; + +const createMainWindow = async (url: string) => { try { log.info('Create window'); @@ -92,7 +138,7 @@ const createWindow = async () => { defaultWidth: 1200, defaultHeight: 800, }); - log.info('load window state with:', mainWindow); + log.info('load window state with:', mainWindowState); const getAssetPath = (...paths: string[]): string => { return path.join(RESOURCES_PATH, ...paths); @@ -104,32 +150,10 @@ const createWindow = async () => { height: mainWindowState.height, width: mainWindowState.width, icon: getAssetPath('icon.png'), - webPreferences: { - nodeIntegration: false, - preload: app.isPackaged - ? path.join(__dirname, 'preload.js') - : path.join(__dirname, '../../.erb/dll/preload.js'), - }, - }); - - // 方案一: 通过文件协议加载界面 - // const url = resolveHtmlPath('index.html'); - // log.info('loadUrl:', url); - // mainWindow.loadURL(url); - - // 方案二: 通过本地起一个http代理服务,然后electron访问http服务 - // log.info('Starting Local Http Server'); - // const url = await getMainWindowUrl(); - // log.info('Local Server started, entry:', url); - - // 方案三: 直接访问远程服务 - log.info('Start with remote server', { - FE_URL: process.env.FE_URL, - SERVICE_URL: process.env.SERVICE_URL, + webPreferences, }); - const url = process.env.FE_URL || process.env.SERVICE_URL; - mainWindow.loadURL(url as string); + mainWindow.loadURL(url); /** * 如果存在 @@ -209,14 +233,14 @@ app.on('window-all-closed', () => { app .whenReady() .then(() => { - createWindow(); + createWelcomeWindow(); initScreenshots(); app.on('activate', () => { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. - if (mainWindow === null) { - createWindow(); + if (welcomeWindow === null && mainWindow === null) { + createWelcomeWindow(); } }); }) diff --git a/client/desktop/src/main/preload.ts b/client/desktop/src/main/preload.ts index 797dfa8b..188c401a 100644 --- a/client/desktop/src/main/preload.ts +++ b/client/desktop/src/main/preload.ts @@ -5,7 +5,11 @@ import { webFrame, } from 'electron'; -export type Channels = 'ipc-example' | 'webview-message'; +export type Channels = + | 'ipc-example' + | 'webview-message' + | 'close' + | 'selectServer'; contextBridge.exposeInMainWorld('electron', { ipcRenderer: { diff --git a/client/desktop/src/main/util.ts b/client/desktop/src/main/util.ts index 3705cf92..ddea9da5 100644 --- a/client/desktop/src/main/util.ts +++ b/client/desktop/src/main/util.ts @@ -1,40 +1,16 @@ +import log from 'electron-log'; import { URL } from 'url'; import path from 'path'; -import is from 'electron-is'; -import { startLocalServer } from './lib/http'; -import getPort, { makeRange } from 'get-port'; -import log from 'electron-log'; - -export let resolveHtmlPath: (htmlFileName: string) => string; -if (process.env.NODE_ENV === 'development') { - const port = process.env.PORT || 1212; - resolveHtmlPath = (htmlFileName: string) => { +export function resolveHtmlPath(htmlFileName: string) { + if (process.env.NODE_ENV === 'development') { + const port = process.env.PORT || 1212; const url = new URL(`http://localhost:${port}`); url.pathname = htmlFileName; return url.href; - }; -} else { - resolveHtmlPath = (htmlFileName: string) => { - return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`; - }; -} - -/** - * 获取主界面的窗口地址 - */ -export async function getMainWindowUrl() { - if (is.dev()) { - const port = process.env.PORT || 1212; - return `http://localhost:${port}/index.html`; - } else { - const port = await getPort({ - port: makeRange(11000, 20000), // 使用高位端口 - }); - await startLocalServer(path.resolve(__dirname, '../renderer/'), port); - - return `http://localhost:${port}/index.html`; } + + return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`; } export function getDefaultLoggerPath(): string { diff --git a/client/desktop/src/renderer/App.css b/client/desktop/src/renderer/App.css index 616d9a48..0a6f4c38 100644 --- a/client/desktop/src/renderer/App.css +++ b/client/desktop/src/renderer/App.css @@ -42,16 +42,8 @@ li { list-style: none; } -a { - text-decoration: none; - height: fit-content; - width: fit-content; - margin: 10px; -} - -a:hover { - opacity: 1; - text-decoration: none; +button + button { + margin-left: 10px; } .Hello { diff --git a/client/desktop/src/renderer/App.tsx b/client/desktop/src/renderer/App.tsx index f0179788..984c9062 100644 --- a/client/desktop/src/renderer/App.tsx +++ b/client/desktop/src/renderer/App.tsx @@ -1,4 +1,3 @@ -import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; import icon from '../../assets/icon.svg'; import './App.css'; @@ -6,45 +5,44 @@ const Hello = () => { return (