mirror of
https://github.com/upscayl/upscayl.git
synced 2024-11-24 07:30:19 +01:00
Updated project
This commit is contained in:
commit
6c18b7eeb3
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [iamWing]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: ['https://paypal.me/iamWing0w0']
|
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.vscode/
|
||||||
|
coverage/
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# OS X files
|
||||||
|
.DS_Store
|
||||||
|
._*
|
165
CHANGELOG_PRE_V4.md
Normal file
165
CHANGELOG_PRE_V4.md
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
# Changelog (pre `v4`)
|
||||||
|
All notable changes up to `v3.0.0` are documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [3.0.0] - 2020-12-26
|
||||||
|
### Added
|
||||||
|
- NPM packages `react-router` & `react-router-dom` to package dependencies.
|
||||||
|
- NPM package `eslint-plugin-react-hooks` to `devDependencies` as required by
|
||||||
|
the updated version of `eslint-config-airbnb`.
|
||||||
|
- NPM package `copy-webpack-plugin` to `devDependencies` as replacement of
|
||||||
|
`copy-pkg-json-webpack-plugin`.
|
||||||
|
- NPM packages `@typescript-eslint/eslint-plugin` & `@typescript-eslint/parser`
|
||||||
|
to `devDependencies` for using ESLint to lint TypeScript.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- __Project is now being developed based on Node.js `v14 (LTS)`.__
|
||||||
|
- Minor version upgrades on package dependencies:
|
||||||
|
- `eslint-import-resolver-webpack` - `0.11.1` -> `0.13.0`
|
||||||
|
- `eslint-plugin-import` - `2.18.2` -> `2.22.1`
|
||||||
|
- `eslint-plugin-jsx-a11y` - `6.2.3` -> `6.4.1`
|
||||||
|
- `eslint-plugin-react` - `7.17.0` -> ``7.21.5`
|
||||||
|
- `lodash` - `4.17.15` -> `4.17.20`
|
||||||
|
- Major version upgrades on package dependencies:
|
||||||
|
- `react` & `react-dom` - `16.12.0` -> `17.0.1`
|
||||||
|
- `cross-env` - `5.2.1` -> `7.0.3`
|
||||||
|
- `css-loader` - `1.0.1` -> `5.0.1`
|
||||||
|
- `electron` - `3.1.13` -> `11.1.1`
|
||||||
|
- `electron-builder` - `20.44.4` -> `22.9.1`
|
||||||
|
- `eslint` - `5.16.0` -> `7.16.0`
|
||||||
|
- `eslint-config-airbnb` - `17.1.1` -> `18.2.1`
|
||||||
|
- `file-loader` - `2.0.0` -> `6.2.0`
|
||||||
|
- `html-webpack-plugin` - `3.2.0` -> `4.5.0`
|
||||||
|
- `mocha` - `5.2.0` -> `8.2.1`
|
||||||
|
- `rimraf` - `2.7.1` -> `3.0.2`
|
||||||
|
- `source-map-loader` - `0.2.4` -> `2.0.0`
|
||||||
|
- `spectron` - `5.0.0` -> `13.0.0`
|
||||||
|
- `style-loader` - `0.23.1` -> `2.0.0`
|
||||||
|
- `ts-loader` - `5.4.5` -> `8.0.12`
|
||||||
|
- `ts-node` - `7.0.1` -> `9.1.1`
|
||||||
|
- `typescript` - `3.7.2` - > `4.1.3`
|
||||||
|
- `webpack` - `4.41.2` -> `5.11.0`
|
||||||
|
- `webpack-cli` - `3.3.10` -> `4.3.0`
|
||||||
|
- Moved `@types` packages from `dependencies` to `devDependencies` as those
|
||||||
|
have no need to be included in production builds.
|
||||||
|
- Commands of NPM scripts `dev` & `prod` to make them work with Webpack 5.
|
||||||
|
- Migrated to ESLint from TSLint.
|
||||||
|
- Updated prefix of internal paths from `@` to `_` to avoid confusions with
|
||||||
|
scoped NPM packages.
|
||||||
|
- Indentation for `.ts` files is now set as `2` spaces instead of `4`.
|
||||||
|
- `electron-builder` settings:
|
||||||
|
- `win`:
|
||||||
|
- Enabled `asar`. _(turn it off if the executable built doesn't work)_
|
||||||
|
- Disabled one click installer.
|
||||||
|
- Allowed custom installation directory in the installer built.
|
||||||
|
- `mac`:
|
||||||
|
- Changed build target back to `dmg`.
|
||||||
|
- `buildVersion` is now being used as build number instead of just another
|
||||||
|
parameter for semantic version number.
|
||||||
|
- Updated section `Known issues` in `README`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- ESLint errors/warnings on `main.ts` & `renderer.tsx`.
|
||||||
|
- `electron-builder` fails to build `dmg` on `macOS` (issue
|
||||||
|
[electron-builder #3990])by upgrading the package version to `> 21.2.0`.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- NPM package `copy-pkg-json-webpack-plugin` as it doesn't work with Webpack 5
|
||||||
|
and seems not very well maintained.
|
||||||
|
- NPM packages `acorn` & `ajv` from `devDependencies` as they're not being used
|
||||||
|
in this boilerplate.
|
||||||
|
- NPM packages `tslint` & `tslint-microsoft-contrib` as TSLint is now
|
||||||
|
deprecated.
|
||||||
|
|
||||||
|
## [2.0.2] - 2019-12-02
|
||||||
|
`v2.0.2` is a minor hotfix release fixed the documentation error and build
|
||||||
|
error on `macOS Catalina(10.15+)`.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Extended README section `Getting started` with `npm start` command
|
||||||
|
description.
|
||||||
|
- README section `Known issue`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Minor version upgrades on package dependencies.
|
||||||
|
- `macOS` build target to `pkg` from default `dmg` due to no 32-bit apps
|
||||||
|
support from `macOS Catalina` that caused `electron-builder` fails to build
|
||||||
|
`dmg` image on `macOS` prior to `electron-builder@21.2.0`.
|
||||||
|
_`pkg` build is unaffected and is used as a workaround for the current version
|
||||||
|
prior to the major version upgrades on dependencies in next release._
|
||||||
|
_Related issue: [electron-builder #3990](https://github.com/electron-userland/electron-builder/issues/3990)_
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- [Issue #2] - incorrect command `npm run install` to `npm install` in `README`.
|
||||||
|
|
||||||
|
## [2.0.1] - 2018-02-05
|
||||||
|
`v2.0.1` is a minor hotfix release patched the `NODE_ENV` not set on Windows
|
||||||
|
issue.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Package `cross-env` as `devDependencies`.
|
||||||
|
- README section "Author".
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- NPM scripts won't set environment variables on Windows issue.
|
||||||
|
|
||||||
|
## 2.0.0 - 2018-02-04
|
||||||
|
`v2.0.0` is a major release that most part of the boilerplate has been rewritten.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- ESLint for code checking on Javascript files.
|
||||||
|
- Airbnb's extensible `.eslintrc` package & its' peer dependencies.
|
||||||
|
- `.eslintrc` that extends Airbnb's config and has customised rules configured.
|
||||||
|
- _Rule `no-default-export` is set for JavaScript files to align with
|
||||||
|
TypeScript._
|
||||||
|
- ESLint plugin `eslint-import-resolver-webpack` for ESLint to resolve path
|
||||||
|
aliases set in Webpack config.
|
||||||
|
- Webpack plugin `copy-pkg-json-webpack-plugin` to generate a `package.json`
|
||||||
|
file and pack into Webpack's bundled package for production.
|
||||||
|
- Build commands `build:mac` & `build:win` to package & build the installer of
|
||||||
|
your Electron app for macOS & Windows using `electron-builder`.
|
||||||
|
- README section "Building the installer for your Electron app" & sub-section
|
||||||
|
"Extra options".
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Refactored Webpack config file to have `mainConfig` & `rendererConfig`
|
||||||
|
cleaned up, and set mode by environment variable.
|
||||||
|
- `.gitignore` to ignore folder `out/` which will be auto-generated during the
|
||||||
|
build process.
|
||||||
|
- README section "How does it work?" is now renamed to "Getting started" &
|
||||||
|
completed the documentation of this section.
|
||||||
|
- README section "Folder structure" to reflect the changes in `v2.0.0`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- CSS files fail to inject into views issue by setting Webpack to use
|
||||||
|
`style-loader` alongside with `css-loader` to bundle the files in Webpack
|
||||||
|
config file.
|
||||||
|
- `baseUrl` in `tsconfig.json` points to root directory incorrectly issue.
|
||||||
|
Corrected to current directory so VSCode can resolves the path aliases
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Redux & React-Redux related settings, including packages listed on
|
||||||
|
`devDependencies`, path aliases & folders listed in folder structure.
|
||||||
|
- Since Electron's built-in IPC & basic React states should be enough to get
|
||||||
|
the works done, and most Electron apps which have their application logic
|
||||||
|
runs on Electron's `main` process rather then `renderer` process actually
|
||||||
|
don't need React-Redux, `redux` & `react-redux` are no longer included in
|
||||||
|
this boilerplate.
|
||||||
|
- Redux & React-Redux can still be used on this boilerplate by installing the
|
||||||
|
package yourself. For details, please refer to the corresponding library's
|
||||||
|
documents, there's no different than working on any other project which
|
||||||
|
isn't based on this boilerplate.
|
||||||
|
- Separated Webpack config files for `development` & `production` mode.
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare/v3.0.0...HEAD
|
||||||
|
[2.0.1]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare/v2.0.0...v2.0.1
|
||||||
|
[2.0.2]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare/v2.0.1...v2.0.2
|
||||||
|
[3.0.0]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare/v2.0.2...v3.0.0
|
||||||
|
|
||||||
|
[Issue #2]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/issues/2
|
||||||
|
[electron-builder #3990]: https://github.com/electron-userland/electron-builder/issues/3990
|
91
CHANGELOG_V4+.md
Normal file
91
CHANGELOG_V4+.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# Changelog `v4+`
|
||||||
|
All notable changes to this project on `v4+` will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v4.0.0] - 2022-07-22
|
||||||
|
### Added
|
||||||
|
- Jest as default unit testing framework, with sample test suite for `main`.
|
||||||
|
- Integrated Electron preload pattern.
|
||||||
|
- NPM scripts:
|
||||||
|
- `watch-test` to run Jest in watch mode.
|
||||||
|
- `next-rc`, `next-patch`, `next-minor` & `next-major` for quick
|
||||||
|
package version number advance.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Webpack will now take the module path alias from `tsconfig.json` and set it
|
||||||
|
for you thanks to `tsconfig-paths-webpack-plugin`. Manually set up in Webpack
|
||||||
|
config is no longer needed.
|
||||||
|
- `tsconfig` now configured to use `ES2020` features, with module resolution
|
||||||
|
set to `Node16` to match the NodeJS version used by Electron.
|
||||||
|
- Migrated to the new `createRoot` API introduced in React `v18`.
|
||||||
|
- Some APIs changed in Electron `main` entry script:
|
||||||
|
- `mainWindow` now use `loadFile` API instead of `loadURL`.
|
||||||
|
- Replaced `app.on('ready')` with `app.whenReady()` to align with syntax from
|
||||||
|
[Electron official quick start guide](https://www.electronjs.org/docs/latest/tutorial/quick-start).
|
||||||
|
- `electron-builder` now configured to build `universal` `dmg` for mac, 32 &
|
||||||
|
64 bit `exe` for Windows.
|
||||||
|
- Moved `electron-builder`, Webpack & Webpack-related packages to
|
||||||
|
`optionalDependencies`.
|
||||||
|
- Revamped `README`.
|
||||||
|
- __*Starting from this version, the maintenance schedule will be on a monthly
|
||||||
|
update basis to keep the package dependencies up to date and keep the
|
||||||
|
development going.*__
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Allow to use `import default` statement on non ES modules (e.g. React,
|
||||||
|
lodash) by enabling `esModuleInterop` in `tsconfig`.
|
||||||
|
[#14](https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/issues/14)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Mocha in favour of Jest.
|
||||||
|
- Spectron as it ahs been deprecated.
|
||||||
|
*See - [Spectron Deprecation Notice](https://www.electronjs.org/blog/spectron-deprecation-notice)*
|
||||||
|
|
||||||
|
### Updates on package dependencies
|
||||||
|
### Added
|
||||||
|
- `@types/jest`, `jest`, `ts-jest`, `eslint-plugin-jest` *- Jest support*
|
||||||
|
- `eslint-config-airbnb-typescript` *- Enhance Airbnb's rules in TypeScript*
|
||||||
|
- `ts-config-paths-webpack-plugin` *- Load `tsconfig` path alias into Webpack
|
||||||
|
config*
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
- Major version updates:
|
||||||
|
- `react` & `react-dom` - `17.0.1` -> `18.2.0`
|
||||||
|
- `@types/react` - `17.0.0` -> `18.0.15`
|
||||||
|
- `@types/react-dom` - `17.0.0` -> `18.0.6`
|
||||||
|
- `@typescript-eslint/eslint-plugin` & `@typescript-eslint/parser` -
|
||||||
|
`4.11.0` -> `5.30.7`
|
||||||
|
- `electron` - `11.1.1` -> `19.0.9`
|
||||||
|
- `eslint-config-airbnb` - `18.2.1` -> `19.0.4`
|
||||||
|
- `copy-webpack-plugin` - `7.0.0` -> `11.0.0`
|
||||||
|
- `css-loader` - `5.0.1` -> `6.7.1`
|
||||||
|
- `electron-builder` - `22.9.1` -> `23.1.0`
|
||||||
|
- `html-webpack-plugin` - `4.5.0` -> `5.5.0`
|
||||||
|
- `style-loader` - `2.0.0` -> `3.3.1`
|
||||||
|
- `ts-loader` - `8.0.12` -> `9.3.1`
|
||||||
|
- `ts-config-paths` - `3.9.0` -> `4.0.0`
|
||||||
|
- Minor & patch version updates:
|
||||||
|
- `eslint-import-resolver-webpack` - `0.13.0` -> `0.13.2`
|
||||||
|
- `eslint-plugin-import` - `2.22.1` -> `2.26.0`
|
||||||
|
- `eslint-plugin-jsx-a11y` - `6.4.1` -> `6.6.1`
|
||||||
|
- `eslint-plugin-react` - `7.21.5` -> `7.30.1`
|
||||||
|
- `eslint-plugin-react-hoots` - `4.2.0` -> `4.6.0`
|
||||||
|
- `lodash` - `4.17.20` -> `4.17.21`
|
||||||
|
- `webpack` - `5.11.0` -> `5.73.0`
|
||||||
|
- `webpack-cli` - `4.3.0` -> `4.10.0`
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- `react-router`, `react-router-dom`, `@types/react-router`,
|
||||||
|
`@types/react-router-dom`
|
||||||
|
|
||||||
|
*\- Not being used in any part of the boilerplate*
|
||||||
|
- `@types/mocha`, `mocha`, `ts-node` *- Replaced by `@types/jest`, `jest` &
|
||||||
|
`ts-jest`*
|
||||||
|
- `spectron` *- Deprecated package; No replacement*
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare/v4.0.0...HEAD
|
||||||
|
[v4.0.0]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare/v3.0.0...v4.0.0
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Devtography
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
275
README.md
Normal file
275
README.md
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
# Electron-React-TypeScript-Webpack-Boilerplate
|
||||||
|
A boilerplate that let you instantly start working on your next [Electron] app
|
||||||
|
in [TypeScript] with no time wasted messing with the config files.
|
||||||
|
|
||||||
|
- Ready to use [Electron] project template with [React], [Webpack] and
|
||||||
|
[TypeScript] seamlessly integrated.
|
||||||
|
- [ESLint] set up with TypeScript, Airbnb's rules, and [Jest] support.
|
||||||
|
- [Jest] integrated and configured.
|
||||||
|
- [`electron-builder`] for app packaging, with basic build config for Windows
|
||||||
|
macOS included.
|
||||||
|
- Clean, easy to read and alter config files. No config file is hidden behind
|
||||||
|
yet another script!
|
||||||
|
- Monthly maintenance to keep things up to date!
|
||||||
|
|
||||||
|
*This boilerplate is tested on the latest macOS and Windows. If anything
|
||||||
|
doesn't work, please [file an issue].*
|
||||||
|
|
||||||
|
### Maintenance schedule
|
||||||
|
Starting from `v4.0.0`, the project maintenance will become much more regular.
|
||||||
|
A new release will be published on a monthly basis to keep the package
|
||||||
|
dependencies, package configurations and APIs / syntax up to date.
|
||||||
|
|
||||||
|
Maintenance work will begin on 1st of each month, and expect the new version to
|
||||||
|
be released within the first week of the month. New features from different
|
||||||
|
tools integrated in this boilerplate might not always be implemented at once,
|
||||||
|
especially on experimental features. If you want any particular feature to be
|
||||||
|
implemented, please [file an issue], or consider make a [new pull request].
|
||||||
|
|
||||||
|
### Development plan
|
||||||
|
- [ ] Create a `create-react-app`-like package initialiser __!!!__
|
||||||
|
- [ ] Integrate another end-to-end testing framework to replace [Spectron]
|
||||||
|
- [ ] Migrate to Webpack 5 `Asset Modules`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 🚧 CAUTION 🚧 🚨
|
||||||
|
- [Spectron] has officially been deprecated by the [Electron] team on
|
||||||
|
February 1, 2022, thus, its' integration has also been dropped from this
|
||||||
|
boilerplate on `v4+`.
|
||||||
|
|
||||||
|
A replacement will be integrated in future version (pending for `v5`).
|
||||||
|
Currently evaluating different options including [Playwright] and
|
||||||
|
[WebdriverIO].
|
||||||
|
|
||||||
|
*See - [Spectron Deprecation Notice]*
|
||||||
|
|
||||||
|
- `mocha` has been dropped and replaced by [Jest] on `v4+`. If you're using
|
||||||
|
`mocha` as your unit testing framework, please reference to `package.json`
|
||||||
|
from [`v3.0.0`].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
1. Clone this repository, or if you're hosting your Electron project on GitHub,
|
||||||
|
click [`Use this template`] to create a new project.
|
||||||
|
|
||||||
|
2. Edit the following fields in `package.json` for your own project:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "your-project-name",
|
||||||
|
"version": "whatever-you-like",
|
||||||
|
"description": "your-own-description",
|
||||||
|
"build": {
|
||||||
|
"appId": "your-app-id",
|
||||||
|
"productName": "your-product-name",
|
||||||
|
"buildVersion": "your-build-number"
|
||||||
|
},
|
||||||
|
"author": "who's-the-author?",
|
||||||
|
"license": "if-you-don't-want-to-use-MIT",
|
||||||
|
"repository": "type-and-link-of-your-repo",
|
||||||
|
"bugs": "issue-page-of-your-repo",
|
||||||
|
"homepage": "homepage-of-your-repo"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `npm install` to install the dependencies.
|
||||||
|
|
||||||
|
*Please note that `optionalDependencies` should only be omitted on your
|
||||||
|
CI/CD pipeline for unit testing. It's meant to save some bandwidth. You'll
|
||||||
|
need all the packages listed for development.*
|
||||||
|
|
||||||
|
Done! Now run `npm run dev` to start the Webpack in development and watch mode.
|
||||||
|
It's time to start working on your project.
|
||||||
|
|
||||||
|
Be aware that starting Webpack will only compile your files to `dist` folder
|
||||||
|
but won't start the Electron app. Use `npm start` command to start your
|
||||||
|
Electron app once the files are compiled.
|
||||||
|
|
||||||
|
__*Starting from `v4.0.0`, you no longer need to manually config your module
|
||||||
|
path alias in `webpack.config.js`. All module path alias set in `tsconfig.json`
|
||||||
|
will be configured in Webpack automatically thanks to [`tsconfig-paths`] and
|
||||||
|
[`tsconfig-paths-webpack-plugin`].*__
|
||||||
|
|
||||||
|
## Build your Electron app package
|
||||||
|
Different from the official [Electron quick start guide], this boilerplate uses
|
||||||
|
[`electron-builder`] instead of [Electron Forge] to package your Electron app.
|
||||||
|
|
||||||
|
By default, the build configuration in `package.json` is configured to build the
|
||||||
|
mac universal package (for Apple Silicon & Intel based machines) and Windows
|
||||||
|
`exe` installer (both 32 & 64 bit). You should not need to change anything in
|
||||||
|
the build script other than supplying the app icon unless you need to sign your
|
||||||
|
code/package or build for Linux.
|
||||||
|
|
||||||
|
For code signing and notarization, or to build for Linux, please read
|
||||||
|
[`electron-builder`'s document] for configuring the build script.
|
||||||
|
|
||||||
|
To package your Electron app, run `npm run prod` to get your code compiled in
|
||||||
|
`production` mode, then use `npm run build:(win|mac)` to build the package.
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
- [`electron-builder`] packages the file into Electron's `asar` archive format
|
||||||
|
by default. Based on past experiences with old Electron & `electron-builder`
|
||||||
|
versions, this might lead to runtime error on Windows while launching the
|
||||||
|
installed Electron app.
|
||||||
|
|
||||||
|
One way to verify this issue is to build the mac package and see if your app
|
||||||
|
runs fine on mac. If it's the case, you can override the `asar` archive
|
||||||
|
option in the build configuration in `package.json` by adding `asar: false`
|
||||||
|
in `win` section.
|
||||||
|
|
||||||
|
This solution isn't ideal but since `asar` archiving is
|
||||||
|
meant to improve performance of reading files if bundler like Webpack is not
|
||||||
|
being used. The app packaging workflow defined in this boilerplate already
|
||||||
|
uses Webpack to minify your code in `production` builds, so there shouldn't
|
||||||
|
be any significant performance different with `asar` archiving disabled.
|
||||||
|
|
||||||
|
## Project folders & files
|
||||||
|
- `dist/` - [Webpack] output location
|
||||||
|
|
||||||
|
__Contents will be flushed automatically on execution of `npm run <dev|prod>`
|
||||||
|
script.__
|
||||||
|
|
||||||
|
- `out/` - [`electron-builder`] output location
|
||||||
|
|
||||||
|
- `public/` - Global static assets.
|
||||||
|
- `index.html` - Template for `HTML Webpack Plugin`
|
||||||
|
|
||||||
|
Update the value of `<title>` tag to change the default window title.
|
||||||
|
|
||||||
|
- `style.css` - `CSS` file location sample
|
||||||
|
|
||||||
|
Not much defined in this file. You can either put your `CSS` settings here
|
||||||
|
or use any other tools you prefer.
|
||||||
|
|
||||||
|
- `src/` - Folder for all your source code
|
||||||
|
- `main/` - For modules which run on the `main` process.
|
||||||
|
- `main.ts` - [Electron] `main` process entry point
|
||||||
|
|
||||||
|
- `preload` - Preload scripts go here
|
||||||
|
- `ipc-api.ts` - APIs for IPC between `main` & `renderer`
|
||||||
|
|
||||||
|
Consider convert this module into a collection of submodules if you have
|
||||||
|
many APIs for IPC. See example as below:
|
||||||
|
```ts
|
||||||
|
// ipc-api/index.ts
|
||||||
|
import submoduleA from './submodule-a';
|
||||||
|
import submoduleB from './submodule-b';
|
||||||
|
|
||||||
|
export default { ...submoduleA, ...submoduleB };
|
||||||
|
|
||||||
|
// ipc-api/submodule-a.ts
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
function a { ipcRenderer.send('a'); }
|
||||||
|
|
||||||
|
export default { a };
|
||||||
|
|
||||||
|
// ipc-api/submodule-b.ts
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
function b { ipcRenderer.send('b'); }
|
||||||
|
|
||||||
|
export default { b };
|
||||||
|
```
|
||||||
|
|
||||||
|
- `preload.ts` - [Electron] preload script entry point
|
||||||
|
|
||||||
|
There should be no need to modify this file unless you want to use other
|
||||||
|
key(s) for your IPC APIs. By default, all APIs defined in `ipc-api`
|
||||||
|
module are exposed under key `ipcApi` in `contextBridge`.
|
||||||
|
|
||||||
|
- `renderer/` - Where the frontend scripts stay
|
||||||
|
- `App.tsx` - Root [React] component
|
||||||
|
- `renderer.tsx` - [Electron] `renderer` process entry point
|
||||||
|
|
||||||
|
*`public/style.css` imported here. Change it if you want.*
|
||||||
|
|
||||||
|
- `types/` - Home for self-defined `.d.ts` files
|
||||||
|
- `global.d.ts` - Extends global scope interfaces
|
||||||
|
|
||||||
|
This file includes ambient declaration for calling the IPC APIs defined in
|
||||||
|
`preload/ipc-api` from the `renderer`. Remember __NOT__ to remove this
|
||||||
|
part, otherwise TypeScript will tell you `type not exist`. However, if
|
||||||
|
you've opted to use a different key other than `ipcAPI` in the preload
|
||||||
|
script, __DO__ remember to update this file to match your own settings.
|
||||||
|
|
||||||
|
- `utils/` - Place to store the helper scripts
|
||||||
|
- `node-env.ts` - Shortcut to determine `NODE` environment
|
||||||
|
|
||||||
|
- `tests/` - Unit testing files location
|
||||||
|
|
||||||
|
To avoid test files mixing up with the source code, [Jest] is configured to
|
||||||
|
look for test file(s) within this folder only.
|
||||||
|
|
||||||
|
File name of the test files can either be `[filename].test.tsx` or
|
||||||
|
`[filename].spec.ts(x)`. `js(x)` can also be used for test files, but I assume
|
||||||
|
you'd use TypeScript if you're using this boilerplate.
|
||||||
|
|
||||||
|
- `main/main.spec.ts` - Sample test file for `src/main/main`
|
||||||
|
- `tsconfig.json` - TypeScript config file for `tests` module
|
||||||
|
- `.eslintignore` - [ESLint] ignore file
|
||||||
|
- `.eslintrc` - [ESLint] config file
|
||||||
|
|
||||||
|
Configured to use Airbnb's rules with [TypeScript] supported, and rules for
|
||||||
|
[Jest] applied.
|
||||||
|
|
||||||
|
- `.gitignore` - Git ignore file
|
||||||
|
- `CHANGELOG_PRE_V4.md` - Changelog of this boilerplate prior to `v4.0.0`
|
||||||
|
- `CHANGELOG_V4+.md` - Changelog of this boilerplate from `v4.0.0` onwards
|
||||||
|
- `jest.config.js` - [Jest] config file
|
||||||
|
- `LICENSE` - MIT license
|
||||||
|
- `package-lock.json`
|
||||||
|
- `package.json`
|
||||||
|
|
||||||
|
Includes basic build config for `electron-builder`. It's likely that you'll
|
||||||
|
have to personalise the build config when it comes to the time you're about
|
||||||
|
to release your app. Please read [`electron-builder`'s document] for the
|
||||||
|
build config setup guides.
|
||||||
|
|
||||||
|
- `README.md`
|
||||||
|
- `tsconfig.json` - [TypeScript] config file
|
||||||
|
|
||||||
|
Module path aliases are configured here. [Jest] & [Webpack] will pick up the
|
||||||
|
alias settings here to config their own. No need to manually config in Jest
|
||||||
|
& Webpack again.
|
||||||
|
|
||||||
|
- `webpack.config.json` - [Webpack] config file
|
||||||
|
|
||||||
|
Includes configurations targetting `electron-main`, `electron-preload`, and
|
||||||
|
`electron-renderer` respectively.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
[Wing Chau](https://github.com/iamWing) [@Devtography](https://github.com/Devtography)
|
||||||
|
|
||||||
|
## Donation
|
||||||
|
Maintaining this project takes time, lots of cups of coffee, and I do it for
|
||||||
|
free. Consider buy me coffee via [donations]. 100% of donation will fund my
|
||||||
|
coffee buying budget for quality coffee beans from great roasters I know 😉 ☕️️
|
||||||
|
|
||||||
|
## License
|
||||||
|
Electron React TypeScript Webpack Boilerplate is open source software
|
||||||
|
[licensed as MIT](LICENSE).
|
||||||
|
|
||||||
|
[Electron]: https://www.electronjs.org
|
||||||
|
[React]: https://reactjs.org
|
||||||
|
[Webpack]: https://webpack.js.org
|
||||||
|
[TypeScript]: https://www.typescriptlang.org
|
||||||
|
[ESLint]: http://eslint.org
|
||||||
|
[Jest]: https://jestjs.io
|
||||||
|
[`electron-builder`]: https://github.com/electron-userland/electron-builder
|
||||||
|
[file an issue]: https://www.electronjs.org
|
||||||
|
[new pull request]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/compare
|
||||||
|
[Spectron]: https://github.com/electron-userland/spectron
|
||||||
|
[Playwright]: https://playwright.dev
|
||||||
|
[WebdriverIO]: https://webdriver.io
|
||||||
|
[Spectron Deprecation Notice]: https://www.electronjs.org/blog/spectron-deprecation-notice
|
||||||
|
[`v3.0.0`]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/releases/tag/v3.0.0
|
||||||
|
[`Use this template`]: https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/generate
|
||||||
|
[`tsconfig-paths`]: https://github.com/dividab/tsconfig-paths
|
||||||
|
[`tsconfig-paths-webpack-plugin`]: https://github.com/dividab/tsconfig-paths-webpack-plugin
|
||||||
|
[Electron quick start guide]: https://www.electronjs.org/docs/latest/tutorial/quick-start
|
||||||
|
[Electron Forge]: https://github.com/electron-userland/electron-forge
|
||||||
|
[`electron-builder`'s document]: https://www.electron.build
|
||||||
|
[donations]: https://github.com/sponsors/iamWing
|
30
jest.config.js
Normal file
30
jest.config.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const { pathsToModuleNameMapper } = require('ts-jest')
|
||||||
|
const { compilerOptions } = require('./tsconfig.json')
|
||||||
|
|
||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
module.exports = config = {
|
||||||
|
testEnvironment: 'node',
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: 'tsconfig.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
|
||||||
|
moduleNameMapper: pathsToModuleNameMapper(
|
||||||
|
compilerOptions.paths,
|
||||||
|
{ prefix: '<rootDir>/' },
|
||||||
|
),
|
||||||
|
modulePathIgnorePatterns: [
|
||||||
|
'<rootDir>/dist',
|
||||||
|
'<rootDir>/node_modules',
|
||||||
|
'<rootDir>/out',
|
||||||
|
],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(ts|tsx)$': 'ts-jest',
|
||||||
|
},
|
||||||
|
testMatch: [
|
||||||
|
'**/tests/**/*.(spec|test).(ts?(x)|js?(x))',
|
||||||
|
],
|
||||||
|
collectCoverage: true,
|
||||||
|
verbose: true,
|
||||||
|
};
|
89
package.json
Normal file
89
package.json
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "electron-react-typescript-webpack-boilerplate",
|
||||||
|
"version": "4.0.0",
|
||||||
|
"description": "Pre-configured boilerplate for Electron + React + TypeScript",
|
||||||
|
"main": "./dist/main.bundle.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "electron ./dist/main.bundle.js",
|
||||||
|
"webpack:dev": "rimraf dist && cross-env NODE_ENV=development webpack --watch --progress --color",
|
||||||
|
"prod": "rimraf dist && cross-env NODE_ENV=production webpack --progress --color",
|
||||||
|
"test": "cross-env NODE_ENV=test jest",
|
||||||
|
"watch-test": "npm run test -- --watchAll",
|
||||||
|
"next-rc": "npm --no-git-tag-version version prerelease --preid=rc",
|
||||||
|
"next-patch": "npm --no-git-tag-version version patch",
|
||||||
|
"next-minor": "npm --no-git-tag-version version minor",
|
||||||
|
"next-major": "npm --no-git-tag-version version major",
|
||||||
|
"build:win": "electron-builder build --win",
|
||||||
|
"build:mac": "electron-builder build --mac"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "com.devtography.electron_boilerplate",
|
||||||
|
"productName": "Electron+React+TypeScript Boilerplate",
|
||||||
|
"directories": {
|
||||||
|
"app": "./dist/",
|
||||||
|
"output": "./out/"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"target": {
|
||||||
|
"target": "dmg",
|
||||||
|
"arch": "universal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"target": {
|
||||||
|
"target": "nsis",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"ia32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": false,
|
||||||
|
"allowToChangeInstallationDirectory": true
|
||||||
|
},
|
||||||
|
"buildVersion": "1"
|
||||||
|
},
|
||||||
|
"author": "Wing Chau @Devtography",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tensorflow/tfjs": "^3.19.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-dropzone": "^14.2.2",
|
||||||
|
"upscaler": "^1.0.0-beta.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^28.1.6",
|
||||||
|
"@types/react": "^18.0.15",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"electron": "^19.0.9",
|
||||||
|
"jest": "^28.1.3",
|
||||||
|
"ts-jest": "^28.0.7",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
|
"electron-builder": "^23.1.0",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"ts-loader": "^9.3.1",
|
||||||
|
"tsconfig-paths": "^4.0.0",
|
||||||
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||||
|
"webpack": "^5.73.0",
|
||||||
|
"webpack-cli": "^4.10.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Devtography/electron-react-typescript-webpack-boilerplate"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Devtography/electron-react-typescript-webpack-boilerplate/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/Devtography/electron-react-typescript-webpack-boilerplate#readme"
|
||||||
|
}
|
13
public/index.html
Normal file
13
public/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Hello World!</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app" class="app"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
3
public/style.css
Normal file
3
public/style.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.app {
|
||||||
|
color: grey;
|
||||||
|
}
|
68
src/main/main.ts
Normal file
68
src/main/main.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Entry point of the Election app.
|
||||||
|
*/
|
||||||
|
import * as path from "path";
|
||||||
|
import * as nodeEnv from "_utils/node-env";
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
import { BrowserWindow, app, ipcMain } from "electron";
|
||||||
|
|
||||||
|
let mainWindow: Electron.BrowserWindow | undefined;
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
// Create the browser window.
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
height: 600,
|
||||||
|
width: 800,
|
||||||
|
webPreferences: {
|
||||||
|
devTools: nodeEnv.dev,
|
||||||
|
preload: path.join(__dirname, "./preload.bundle.js"),
|
||||||
|
webSecurity: nodeEnv.prod,
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// and load the index.html of the app.
|
||||||
|
mainWindow.loadFile("index.html").finally(() => {
|
||||||
|
/* no action */
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emitted when the window is closed.
|
||||||
|
mainWindow.on("closed", () => {
|
||||||
|
mainWindow = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app.commandLine.appendSwitch("ignore-gpu-blacklist");
|
||||||
|
app
|
||||||
|
.whenReady()
|
||||||
|
.then(() => {
|
||||||
|
if (nodeEnv.dev || nodeEnv.prod) createWindow();
|
||||||
|
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (BrowserWindow.getAllWindows.length === 0) createWindow();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
/* no action */
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("window-all-closed", () => {
|
||||||
|
if (process.platform !== "darwin") app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// const getUpscaledImage = async () => {
|
||||||
|
// const file = fs.readFileSync(path.resolve(__dirname, "./image.png"));
|
||||||
|
// const image = tf.node.decodeImage(file, 3);
|
||||||
|
// const tensor = await upscaler.upscale(image, {
|
||||||
|
// output: "tensor",
|
||||||
|
// patchSize: 64,
|
||||||
|
// padding: 6,
|
||||||
|
// });
|
||||||
|
// image.dispose();
|
||||||
|
// const upscaledTensor = await tf.node.encodePng(tensor);
|
||||||
|
// tensor.dispose();
|
||||||
|
// return upscaledTensor;
|
||||||
|
// };
|
||||||
|
|
||||||
|
ipcMain.on("hello", async () => {});
|
||||||
|
|
||||||
|
export const exportedForTests = nodeEnv.test ? { createWindow } : undefined;
|
4
src/preload/preload.ts
Normal file
4
src/preload/preload.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { contextBridge } from "electron";
|
||||||
|
import { ipcRenderer } from "electron";
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer);
|
121
src/renderer/App.css
Normal file
121
src/renderer/App.css
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
body {
|
||||||
|
padding: 40px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#root {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzone {
|
||||||
|
cursor: move; /* fallback if grab cursor is unsupported */
|
||||||
|
cursor: grab;
|
||||||
|
cursor: -moz-grab;
|
||||||
|
cursor: -webkit-grab;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
padding: 40px;
|
||||||
|
border: 5px dotted #ddd;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.original-image {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.original img {
|
||||||
|
transition-duration: 1.2s;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: #ddd;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
}
|
||||||
|
button:first-child {
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
button:last-child {
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.active {
|
||||||
|
background: #c4c4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.display .image-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display .scaled-up img {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragOverlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragger {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 100%;
|
||||||
|
width: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
position: absolute;
|
||||||
|
transform: translate(50px 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
.interpolation {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
}
|
182
src/renderer/App.tsx
Normal file
182
src/renderer/App.tsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import Upscaler from "upscaler";
|
||||||
|
import React, { useCallback, useState, useEffect, useRef } from "react";
|
||||||
|
import { useDropzone } from "react-dropzone";
|
||||||
|
import "./App.css";
|
||||||
|
|
||||||
|
const upscaler = new Upscaler();
|
||||||
|
function App() {
|
||||||
|
const [src, setSrc] = useState<string>();
|
||||||
|
const [originalSize, setOriginalSize] = useState({ width: 0, height: 0 });
|
||||||
|
const [scale, setScale] = useState(1);
|
||||||
|
const [interpolation, setInterpolation] = useState("bicubic");
|
||||||
|
const [upscaledImageSrc, setUpscaledImageSrc] = useState<string>();
|
||||||
|
const [displayUpscaledImageSrc, setDisplayUpscaledImageSrc] = useState(false);
|
||||||
|
const [dragX, setDragX] = useState(0.5);
|
||||||
|
const [dragging, setDragging] = useState(false);
|
||||||
|
const container = useRef<HTMLDivElement>();
|
||||||
|
const onDrop = useCallback((acceptedFiles) => {
|
||||||
|
const file = acceptedFiles[0];
|
||||||
|
const fr = new FileReader();
|
||||||
|
fr.onload = async () => {
|
||||||
|
setSrc(fr.result as string);
|
||||||
|
};
|
||||||
|
fr.readAsDataURL(file);
|
||||||
|
}, []);
|
||||||
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
|
||||||
|
useEffect(() => {
|
||||||
|
if (src) {
|
||||||
|
const img = new Image();
|
||||||
|
img.crossOrigin = "anonymous";
|
||||||
|
img.src = src;
|
||||||
|
img.onload = () => {
|
||||||
|
upscaler.upscale(img).then(setUpscaledImageSrc);
|
||||||
|
const width = img.width;
|
||||||
|
const height = img.height;
|
||||||
|
setOriginalSize({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [src]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (originalSize) {
|
||||||
|
let upscaledImageSrcTimer;
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setScale(3);
|
||||||
|
upscaledImageSrcTimer = setTimeout(() => {
|
||||||
|
setDisplayUpscaledImageSrc(true);
|
||||||
|
}, 1200);
|
||||||
|
}, 300);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
clearTimeout(upscaledImageSrcTimer);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [originalSize]);
|
||||||
|
|
||||||
|
const startDragging = () => {
|
||||||
|
setDragging(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drag = (e) => {
|
||||||
|
if (dragging) {
|
||||||
|
const offsetWidth = container.current.offsetWidth;
|
||||||
|
const x = e.clientX - (window.innerWidth - offsetWidth) / 2 - 10;
|
||||||
|
setDragX(x / offsetWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopDragging = () => {
|
||||||
|
console.log("stop");
|
||||||
|
setDragging(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
const left = dragX * 100;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="original-image"
|
||||||
|
style={{
|
||||||
|
width: originalSize ? originalSize.width * scale : null,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="header">
|
||||||
|
{displayUpscaledImageSrc && (
|
||||||
|
<>
|
||||||
|
<div className="interpolation">
|
||||||
|
<button
|
||||||
|
className={interpolation === "none" ? "active" : null}
|
||||||
|
onClick={() => setInterpolation("none")}
|
||||||
|
>
|
||||||
|
None
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={interpolation === "bicubic" ? "active" : null}
|
||||||
|
onClick={() => setInterpolation("bicubic")}
|
||||||
|
>
|
||||||
|
Bicubic interpolation
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>Upscaled image</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="display"
|
||||||
|
style={{
|
||||||
|
width: originalSize ? originalSize.width * scale : null,
|
||||||
|
height: originalSize ? originalSize.height * scale : null,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{displayUpscaledImageSrc && (
|
||||||
|
<div
|
||||||
|
className="dragOverlay"
|
||||||
|
ref={container}
|
||||||
|
onMouseMove={drag}
|
||||||
|
onMouseUp={stopDragging}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="dragger"
|
||||||
|
onMouseDown={startDragging}
|
||||||
|
style={{
|
||||||
|
left: `calc(${left}%)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="image-container original">
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
alt="Original"
|
||||||
|
width={originalSize ? originalSize.width * scale : null}
|
||||||
|
style={{
|
||||||
|
imageRendering: interpolation === "none" ? "pixelated" : null,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{displayUpscaledImageSrc && (
|
||||||
|
<div
|
||||||
|
className="image-container scaled-up"
|
||||||
|
style={{
|
||||||
|
width: `${100 - left}%`,
|
||||||
|
left: `${left}%`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
left: ((originalSize.width * scale * left) / 100) * -1,
|
||||||
|
}}
|
||||||
|
alt="Upscaled"
|
||||||
|
src={upscaledImageSrc}
|
||||||
|
width={originalSize ? originalSize.width * scale : null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{displayUpscaledImageSrc && <p>Resized to 3x</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dropzone" {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
{isDragActive ? (
|
||||||
|
<p>Drop the files here ...</p>
|
||||||
|
) : (
|
||||||
|
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<div className="app">
|
||||||
|
<h1>React Demo Integration</h1>
|
||||||
|
<App />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
BIN
src/renderer/image.jpg
Normal file
BIN
src/renderer/image.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
13
src/renderer/renderer.tsx
Normal file
13
src/renderer/renderer.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* React renderer.
|
||||||
|
*/
|
||||||
|
// Import the styles here to process them with webpack
|
||||||
|
import "_public/style.css";
|
||||||
|
|
||||||
|
import App from "_renderer/App";
|
||||||
|
import * as React from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
|
const container = document.getElementById("app");
|
||||||
|
const root = createRoot(container!);
|
||||||
|
root.render(<App />);
|
10
src/types/global.d.ts
vendored
Normal file
10
src/types/global.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { ipcRenderer } from "electron";
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
/** APIs for Electron IPC */
|
||||||
|
ipcRenderer?: typeof ipcRenderer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes TS sees this as an external modules so we can extend the global scope.
|
||||||
|
export {};
|
12
src/utils/node-env.ts
Normal file
12
src/utils/node-env.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Predefined NODE_ENV values
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** `NODE_ENV=production` */
|
||||||
|
export const prod = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
/** `NODE_ENV=development` */
|
||||||
|
export const dev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
/** `NODE_ENV=test` */
|
||||||
|
export const test = process.env.NODE_ENV === 'test';
|
25
tests/main/main.spec.ts
Normal file
25
tests/main/main.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { exportedForTests } from '_main/main';
|
||||||
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
jest.mock('electron', () => ({
|
||||||
|
app: {
|
||||||
|
on: jest.fn(),
|
||||||
|
whenReady: jest.fn(() => Promise.resolve()),
|
||||||
|
},
|
||||||
|
ipcMain: { on: jest.fn() },
|
||||||
|
BrowserWindow: jest.fn().mockImplementation(() => ({
|
||||||
|
loadFile: jest.fn(() => Promise.resolve()),
|
||||||
|
on: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('Private props exported for unit tests', () => {
|
||||||
|
expect(exportedForTests).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('func createWindow()', () => {
|
||||||
|
const { createWindow } = exportedForTests!;
|
||||||
|
|
||||||
|
createWindow();
|
||||||
|
expect(BrowserWindow).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
7
tests/tsconfig.json
Normal file
7
tests/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["./**/*"]
|
||||||
|
}
|
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": false,
|
||||||
|
"allowJs": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"module": "ES2020",
|
||||||
|
"moduleResolution": "Node16",
|
||||||
|
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"paths": {
|
||||||
|
"_/*": ["src/*"],
|
||||||
|
"_public/*": ["public/*"],
|
||||||
|
"_main/*": ["src/main/*"],
|
||||||
|
"_preload/*": ["src/preload/*"],
|
||||||
|
"_renderer/*": ["src/renderer/*"],
|
||||||
|
"_types/*": ["src/types/*"],
|
||||||
|
"_utils/*": ["src/utils/*"],
|
||||||
|
"_tests/*": ["tests/*"]
|
||||||
|
},
|
||||||
|
"typeRoots": ["./node_modules/@types", "./src/types"],
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"target": "ES2020"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "tests/**/*"]
|
||||||
|
}
|
95
webpack.config.js
Normal file
95
webpack.config.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
const lodash = require('lodash');
|
||||||
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
function srcPaths(src) {
|
||||||
|
return path.join(__dirname, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEnvProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isEnvDevelopment = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
// #region Common settings
|
||||||
|
const commonConfig = {
|
||||||
|
devtool: isEnvDevelopment ? 'source-map' : false,
|
||||||
|
mode: isEnvProduction ? 'production' : 'development',
|
||||||
|
output: { path: srcPaths('dist') },
|
||||||
|
node: { __dirname: false, __filename: false },
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.json', '.ts', '.tsx'],
|
||||||
|
plugins: [new TsconfigPathsPlugin({
|
||||||
|
configFile: './tsconfig.json',
|
||||||
|
extensions: ['.js', '.json', '.ts', '.tsx'],
|
||||||
|
})],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(ts|tsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'ts-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(scss|css)$/,
|
||||||
|
use: ['style-loader', 'css-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(jpg|png|svg|ico|icns)$/,
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
name: '[path][name].[ext]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
const mainConfig = lodash.cloneDeep(commonConfig);
|
||||||
|
mainConfig.entry = './src/main/main.ts';
|
||||||
|
mainConfig.target = 'electron-main';
|
||||||
|
mainConfig.output.filename = 'main.bundle.js';
|
||||||
|
mainConfig.plugins = [
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: 'package.json',
|
||||||
|
to: 'package.json',
|
||||||
|
transform: (content, _path) => {
|
||||||
|
const jsonContent = JSON.parse(content);
|
||||||
|
const electronVersion = jsonContent.devDependencies.electron;
|
||||||
|
|
||||||
|
delete jsonContent.devDependencies;
|
||||||
|
delete jsonContent.optionalDependencies;
|
||||||
|
delete jsonContent.scripts;
|
||||||
|
delete jsonContent.build;
|
||||||
|
|
||||||
|
jsonContent.main = './main.bundle.js';
|
||||||
|
jsonContent.scripts = { start: 'electron ./main.bundle.js' };
|
||||||
|
jsonContent.devDependencies = { electron: electronVersion }
|
||||||
|
|
||||||
|
return JSON.stringify(jsonContent, undefined, 2);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const preloadConfig = lodash.cloneDeep(commonConfig);
|
||||||
|
preloadConfig.entry = './src/preload/preload.ts';
|
||||||
|
preloadConfig.target = 'electron-preload';
|
||||||
|
preloadConfig.output.filename = 'preload.bundle.js';
|
||||||
|
|
||||||
|
const rendererConfig = lodash.cloneDeep(commonConfig);
|
||||||
|
rendererConfig.entry = './src/renderer/renderer.tsx';
|
||||||
|
rendererConfig.target = 'electron-renderer';
|
||||||
|
rendererConfig.output.filename = 'renderer.bundle.js';
|
||||||
|
rendererConfig.plugins = [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: path.resolve(__dirname, './public/index.html'),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = [mainConfig, preloadConfig, rendererConfig];
|
Loading…
Reference in New Issue
Block a user