Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ade1b8f69c | |||
| 06914b3af4 | |||
| 95c52d8a11 |
@@ -1,4 +0,0 @@
|
|||||||
node_modules
|
|
||||||
release/app/node_modules
|
|
||||||
release/app/dist
|
|
||||||
src/server/node_modules
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
"no-console": "off",
|
|
||||||
"global-require": "off",
|
|
||||||
"import/no-dynamic-require": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/**
|
|
||||||
* Base webpack config used across other specific configs
|
|
||||||
*/
|
|
||||||
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import { dependencies as externals } from '../../release/app/package.json';
|
|
||||||
import webpackPaths from './webpack.paths';
|
|
||||||
|
|
||||||
const configuration: webpack.Configuration = {
|
|
||||||
externals: [...Object.keys(externals || {})],
|
|
||||||
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
exclude: /node_modules/,
|
|
||||||
test: /\.[jt]sx?$/,
|
|
||||||
use: {
|
|
||||||
loader: 'ts-loader',
|
|
||||||
options: {
|
|
||||||
// Remove this line to enable type checking in webpack builds
|
|
||||||
transpileOnly: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
output: {
|
|
||||||
// https://github.com/webpack/webpack/issues/1114
|
|
||||||
library: {
|
|
||||||
type: 'commonjs2',
|
|
||||||
},
|
|
||||||
|
|
||||||
path: webpackPaths.srcPath,
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
new webpack.EnvironmentPlugin({
|
|
||||||
NODE_ENV: 'production',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the array of extensions that should be used to resolve modules.
|
|
||||||
*/
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
|
|
||||||
fallback: {
|
|
||||||
child_process: false,
|
|
||||||
},
|
|
||||||
modules: [webpackPaths.srcPath, 'node_modules'],
|
|
||||||
},
|
|
||||||
|
|
||||||
stats: 'errors-only',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default configuration;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
/* eslint import/no-unresolved: off, import/no-self-import: off */
|
|
||||||
|
|
||||||
module.exports = require('./webpack.config.renderer.dev').default;
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* Webpack config for production electron main process
|
|
||||||
*/
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import TerserPlugin from 'terser-webpack-plugin';
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
|
||||||
import { merge } from 'webpack-merge';
|
|
||||||
|
|
||||||
import checkNodeEnv from '../scripts/check-node-env';
|
|
||||||
import deleteSourceMaps from '../scripts/delete-source-maps';
|
|
||||||
import baseConfig from './webpack.config.base';
|
|
||||||
import webpackPaths from './webpack.paths';
|
|
||||||
|
|
||||||
checkNodeEnv('production');
|
|
||||||
deleteSourceMaps();
|
|
||||||
|
|
||||||
const devtoolsConfig =
|
|
||||||
process.env.DEBUG_PROD === 'true'
|
|
||||||
? {
|
|
||||||
devtool: 'source-map',
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const configuration: webpack.Configuration = {
|
|
||||||
...devtoolsConfig,
|
|
||||||
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
|
|
||||||
optimization: {
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
parallel: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
new webpack.EnvironmentPlugin({
|
|
||||||
NODE_ENV: 'production',
|
|
||||||
DEBUG_PROD: false,
|
|
||||||
START_MINIMIZED: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
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-preload',
|
|
||||||
|
|
||||||
entry: path.join(webpackPaths.srcMainPath, 'preload.ts'),
|
|
||||||
|
|
||||||
output: {
|
|
||||||
path: webpackPaths.dllPath,
|
|
||||||
filename: 'preload.js',
|
|
||||||
},
|
|
||||||
|
|
||||||
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);
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/**
|
|
||||||
* Builds the DLL for development electron renderer process
|
|
||||||
*/
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import { merge } from 'webpack-merge';
|
|
||||||
|
|
||||||
import { dependencies } from '../../package.json';
|
|
||||||
import checkNodeEnv from '../scripts/check-node-env';
|
|
||||||
import baseConfig from './webpack.config.base';
|
|
||||||
import webpackPaths from './webpack.paths';
|
|
||||||
|
|
||||||
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);
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
import 'webpack-dev-server';
|
|
||||||
import { execSync, spawn } from 'child_process';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
||||||
import webpack from 'webpack';
|
|
||||||
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 port = process.env.PORT || 4343;
|
|
||||||
const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json');
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
const requiredByDLLConfig = module.parent!.filename.includes(
|
|
||||||
'webpack.config.renderer.dev.dll'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warn if the DLL is not built
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
!requiredByDLLConfig &&
|
|
||||||
!(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?css$/,
|
|
||||||
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|svg|jpg|jpeg|gif)$/i,
|
|
||||||
type: 'asset/resource',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
...(requiredByDLLConfig
|
|
||||||
? []
|
|
||||||
: [
|
|
||||||
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...');
|
|
||||||
spawn('npm', ['run', 'start:main'], {
|
|
||||||
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);
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
/**
|
|
||||||
* Build config for electron renderer process
|
|
||||||
*/
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
|
||||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
||||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
||||||
import TerserPlugin from 'terser-webpack-plugin';
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
|
||||||
import { merge } from 'webpack-merge';
|
|
||||||
|
|
||||||
import checkNodeEnv from '../scripts/check-node-env';
|
|
||||||
import deleteSourceMaps from '../scripts/delete-source-maps';
|
|
||||||
import baseConfig from './webpack.config.base';
|
|
||||||
import webpackPaths from './webpack.paths';
|
|
||||||
|
|
||||||
checkNodeEnv('production');
|
|
||||||
deleteSourceMaps();
|
|
||||||
|
|
||||||
const devtoolsConfig =
|
|
||||||
process.env.DEBUG_PROD === 'true'
|
|
||||||
? {
|
|
||||||
devtool: 'source-map',
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const configuration: webpack.Configuration = {
|
|
||||||
...devtoolsConfig,
|
|
||||||
|
|
||||||
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|svg|jpg|jpeg|gif)$/i,
|
|
||||||
type: 'asset/resource',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
optimization: {
|
|
||||||
minimize: true,
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
parallel: true,
|
|
||||||
}),
|
|
||||||
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',
|
|
||||||
}),
|
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
filename: 'index.html',
|
|
||||||
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
|
|
||||||
minify: {
|
|
||||||
collapseWhitespace: true,
|
|
||||||
removeAttributeQuotes: true,
|
|
||||||
removeComments: true,
|
|
||||||
},
|
|
||||||
isBrowser: false,
|
|
||||||
isDevelopment: process.env.NODE_ENV !== 'production',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default merge(baseConfig, configuration);
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
import 'webpack-dev-server';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
|
||||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
||||||
import webpack from 'webpack';
|
|
||||||
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 port = process.env.PORT || 4343;
|
|
||||||
|
|
||||||
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?css$/,
|
|
||||||
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|svg|jpg|jpeg|gif)$/i,
|
|
||||||
type: 'asset/resource',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
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) {
|
|
||||||
return middlewares;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default merge(baseConfig, configuration);
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
const rootPath = path.join(__dirname, '../..');
|
|
||||||
|
|
||||||
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,
|
|
||||||
dllPath,
|
|
||||||
srcPath,
|
|
||||||
srcMainPath,
|
|
||||||
srcRendererPath,
|
|
||||||
releasePath,
|
|
||||||
appPath,
|
|
||||||
appPackagePath,
|
|
||||||
appNodeModulesPath,
|
|
||||||
srcNodeModulesPath,
|
|
||||||
distPath,
|
|
||||||
distMainPath,
|
|
||||||
distRendererPath,
|
|
||||||
buildPath,
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export default 'test-file-stub';
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
"no-console": "off",
|
|
||||||
"global-require": "off",
|
|
||||||
"import/no-dynamic-require": "off",
|
|
||||||
"import/no-extraneous-dependencies": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// 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"'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import chalk from 'chalk';
|
|
||||||
import detectPort from 'detect-port';
|
|
||||||
|
|
||||||
const port = process.env.PORT || '4343';
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import rimraf from 'rimraf';
|
|
||||||
import process from 'process';
|
|
||||||
import webpackPaths from '../configs/webpack.paths';
|
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
const commandMap = {
|
|
||||||
dist: webpackPaths.distPath,
|
|
||||||
release: webpackPaths.releasePath,
|
|
||||||
dll: webpackPaths.dllPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
args.forEach((x) => {
|
|
||||||
const pathToRemove = commandMap[x];
|
|
||||||
if (pathToRemove !== undefined) {
|
|
||||||
rimraf.sync(pathToRemove);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import path from 'path';
|
|
||||||
import rimraf from 'rimraf';
|
|
||||||
import webpackPaths from '../configs/webpack.paths';
|
|
||||||
|
|
||||||
export default function deleteSourceMaps() {
|
|
||||||
rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map'));
|
|
||||||
rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map'));
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import webpackPaths from '../configs/webpack.paths';
|
|
||||||
|
|
||||||
const { srcNodeModulesPath } = webpackPaths;
|
|
||||||
const { appNodeModulesPath } = webpackPaths;
|
|
||||||
|
|
||||||
if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) {
|
|
||||||
fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction');
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
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_ID_PASS' in process.env)) {
|
|
||||||
console.warn(
|
|
||||||
'Skipping notarizing step. APPLE_ID and APPLE_ID_PASS 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_ID_PASS,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
src/i18n
|
|
||||||
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
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extends: ['erb', 'plugin:typescript-sort-keys/recommended'],
|
|
||||||
parserOptions: {
|
|
||||||
createDefaultProgram: true,
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
project: './tsconfig.json',
|
|
||||||
sourceType: 'module',
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
},
|
|
||||||
plugins: ['import', 'sort-keys-fix'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
// A temporary hack related to IDE not resolving correct package.json
|
|
||||||
'import/no-extraneous-dependencies': 'off',
|
|
||||||
'import/no-unresolved': 'error',
|
|
||||||
'import/order': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
alphabetize: {
|
|
||||||
caseInsensitive: true,
|
|
||||||
order: 'asc',
|
|
||||||
},
|
|
||||||
groups: ['builtin', 'external', 'internal', ['parent', 'sibling']],
|
|
||||||
'newlines-between': 'never',
|
|
||||||
pathGroups: [
|
|
||||||
{
|
|
||||||
group: 'external',
|
|
||||||
pattern: 'react',
|
|
||||||
position: 'before',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pathGroupsExcludedImportTypes: ['react'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/prefer-default-export': 'off',
|
|
||||||
'jsx-a11y/click-events-have-key-events': 'off',
|
|
||||||
'jsx-a11y/interactive-supports-focus': 'off',
|
|
||||||
'jsx-a11y/media-has-caption': 'off',
|
|
||||||
'no-await-in-loop': 'off',
|
|
||||||
'no-console': 'off',
|
|
||||||
'no-nested-ternary': 'off',
|
|
||||||
'no-restricted-syntax': 'off',
|
|
||||||
'react/jsx-props-no-spreading': 'off',
|
|
||||||
'react/jsx-sort-props': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
callbacksLast: true,
|
|
||||||
ignoreCase: false,
|
|
||||||
noSortAlphabetically: false,
|
|
||||||
reservedFirst: true,
|
|
||||||
shorthandFirst: true,
|
|
||||||
shorthandLast: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// Since React 17 and typescript 4.1 you can safely disable the rule
|
|
||||||
'react/react-in-jsx-scope': 'off',
|
|
||||||
'sort-keys-fix/sort-keys-fix': 'warn',
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
'import/parsers': {
|
|
||||||
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
||||||
},
|
|
||||||
'import/resolver': {
|
|
||||||
// See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
|
|
||||||
node: {
|
|
||||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
|
||||||
},
|
|
||||||
typescript: {},
|
|
||||||
webpack: {
|
|
||||||
config: require.resolve('./.erb/configs/webpack.config.eslint.ts'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"ignorePatterns": [
|
||||||
|
"node_modules/*",
|
||||||
|
"dist/*",
|
||||||
|
"electron/preload/*",
|
||||||
|
"vite.config.ts",
|
||||||
|
"post-install.js"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:typescript-sort-keys/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"@typescript-eslint",
|
||||||
|
"import",
|
||||||
|
"sort-keys-fix",
|
||||||
|
"promise"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"react-hooks/exhaustive-deps": [
|
||||||
|
"warn",
|
||||||
|
{ "enableDangerousAutofixThisMayCauseInfiniteLoops": true }
|
||||||
|
],
|
||||||
|
"react/jsx-sort-props": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"callbacksLast": true,
|
||||||
|
"ignoreCase": false,
|
||||||
|
"noSortAlphabetically": false,
|
||||||
|
"reservedFirst": true,
|
||||||
|
"shorthandFirst": true,
|
||||||
|
"shorthandLast": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"import/order": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"groups": ["builtin", "external", "internal", ["parent", "sibling"]],
|
||||||
|
"pathGroups": [
|
||||||
|
{
|
||||||
|
"pattern": "react",
|
||||||
|
"group": "external",
|
||||||
|
"position": "before"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pathGroupsExcludedImportTypes": ["react"],
|
||||||
|
"newlines-between": "never",
|
||||||
|
"alphabetize": {
|
||||||
|
"order": "asc",
|
||||||
|
"caseInsensitive": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort-keys-fix/sort-keys-fix": "warn",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"consistent-return": "off",
|
||||||
|
"object-curly-newline": "off",
|
||||||
|
"indent": "off",
|
||||||
|
"no-tabs": "off",
|
||||||
|
"react/jsx-indent": "off",
|
||||||
|
"react/jsx-indent-props": "off",
|
||||||
|
"react/react-in-jsx-scope": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
* 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
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [electron-react-boilerplate, amilajack]
|
|
||||||
patreon: amilajack
|
|
||||||
open_collective: electron-react-boilerplate-594
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: You're having technical issues. 🐞
|
|
||||||
labels: 'bug'
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Please use the following issue template or your issue will be closed -->
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
<!-- If the following boxes are not ALL checked, your issue is likely to be closed -->
|
|
||||||
|
|
||||||
- [ ] Using npm
|
|
||||||
- [ ] Using an up-to-date [`main` branch](https://github.com/electron-react-boilerplate/electron-react-boilerplate/tree/main)
|
|
||||||
- [ ] Using latest version of devtools. [Check the docs for how to update](https://electron-react-boilerplate.js.org/docs/dev-tools/)
|
|
||||||
- [ ] Tried solutions mentioned in [#400](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/400)
|
|
||||||
- [ ] For issue in production release, add devtools output of `DEBUG_PROD=true npm run build && npm start`
|
|
||||||
|
|
||||||
## Expected Behavior
|
|
||||||
|
|
||||||
<!--- What should have happened? -->
|
|
||||||
|
|
||||||
## Current Behavior
|
|
||||||
|
|
||||||
<!--- What went wrong? -->
|
|
||||||
|
|
||||||
## Steps to Reproduce
|
|
||||||
|
|
||||||
<!-- Add relevant code and/or a live example -->
|
|
||||||
<!-- Add stack traces -->
|
|
||||||
|
|
||||||
1.
|
|
||||||
|
|
||||||
2.
|
|
||||||
|
|
||||||
3.
|
|
||||||
|
|
||||||
4.
|
|
||||||
|
|
||||||
## Possible Solution (Not obligatory)
|
|
||||||
|
|
||||||
<!--- Suggest a reason for the bug or how to fix it. -->
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
|
||||||
<!--- Did you make any changes to the boilerplate after cloning it? -->
|
|
||||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
|
||||||
|
|
||||||
## Your Environment
|
|
||||||
|
|
||||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
|
||||||
|
|
||||||
- Node version :
|
|
||||||
- electron-react-boilerplate version or branch :
|
|
||||||
- Operating System and version :
|
|
||||||
- Link to your project :
|
|
||||||
|
|
||||||
<!---
|
|
||||||
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️
|
|
||||||
|
|
||||||
Donations will ensure the following:
|
|
||||||
|
|
||||||
🔨 Long term maintenance of the project
|
|
||||||
🛣 Progress on the roadmap
|
|
||||||
🐛 Quick responses to bug reports and help requests
|
|
||||||
-->
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
name: Question
|
|
||||||
about: Ask a question.❓
|
|
||||||
labels: 'question'
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
<!-- What do you need help with? -->
|
|
||||||
|
|
||||||
<!---
|
|
||||||
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️
|
|
||||||
|
|
||||||
Donations will ensure the following:
|
|
||||||
|
|
||||||
🔨 Long term maintenance of the project
|
|
||||||
🛣 Progress on the roadmap
|
|
||||||
🐛 Quick responses to bug reports and help requests
|
|
||||||
-->
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: You want something added to the boilerplate. 🎉
|
|
||||||
labels: 'enhancement'
|
|
||||||
---
|
|
||||||
|
|
||||||
<!---
|
|
||||||
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️
|
|
||||||
|
|
||||||
Donations will ensure the following:
|
|
||||||
|
|
||||||
🔨 Long term maintenance of the project
|
|
||||||
🛣 Progress on the roadmap
|
|
||||||
🐛 Quick responses to bug reports and help requests
|
|
||||||
-->
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
requiredHeaders:
|
|
||||||
- Prerequisites
|
|
||||||
- Expected Behavior
|
|
||||||
- Current Behavior
|
|
||||||
- Possible Solution
|
|
||||||
- Your Environment
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
name: Publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
# To enable auto publishing to github, update your electron publisher
|
|
||||||
# config in package.json > "build" and remove the conditional below
|
|
||||||
if: ${{ github.repository_owner == 'electron-react-boilerplate' }}
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [macos-latest]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout git repo
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Install Node and NPM
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 16
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
npm install --legacy-peer-deps
|
|
||||||
|
|
||||||
- name: Publish releases
|
|
||||||
env:
|
|
||||||
# These values are used for auto updates signing
|
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
||||||
APPLE_ID_PASS: ${{ secrets.APPLE_ID_PASS }}
|
|
||||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
|
||||||
# This is used for uploading release assets to github
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
npm run postinstall
|
|
||||||
npm run build
|
|
||||||
npm exec electron-builder -- --publish always --win --mac --linux
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
name: Test
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out Git repository
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Install Node.js and NPM
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 16
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- name: npm install
|
|
||||||
run: |
|
|
||||||
npm install --legacy-peer-deps
|
|
||||||
|
|
||||||
- name: npm test
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
npm run package
|
|
||||||
npm run lint
|
|
||||||
npm exec tsc
|
|
||||||
npm test
|
|
||||||
@@ -1,31 +1,29 @@
|
|||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.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
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
# OSX
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
release/app/dist
|
release/app/dist
|
||||||
release/build
|
release/build
|
||||||
.erb/dll
|
.vscode/.debug.env
|
||||||
|
./package-lock.json
|
||||||
.idea
|
pnpm-lock.yaml
|
||||||
npm-debug.log.*
|
yarn.lock
|
||||||
*.css.d.ts
|
|
||||||
*.sass.d.ts
|
|
||||||
*.scss.d.ts
|
|
||||||
|
|
||||||
.env*
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx lint-staged
|
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"trailingComma": "es5",
|
"printWidth": 120,
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"printWidth": 100,
|
"trailingComma": "es5",
|
||||||
"arrowParens": "always"
|
"bracketSpacing": true,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"proseWrap": "preserve"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"processors": ["stylelint-processor-styled-components"],
|
|
||||||
"customSyntax": "postcss-scss",
|
|
||||||
"extends": [
|
|
||||||
"stylelint-config-standard-scss",
|
|
||||||
"stylelint-config-styled-components",
|
|
||||||
"stylelint-config-rational-order"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"color-function-notation": ["legacy"],
|
|
||||||
"declaration-empty-line-before": null,
|
|
||||||
"order/properties-order": [],
|
|
||||||
"plugin/rational-order": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"border-in-box-model": false,
|
|
||||||
"empty-line-between-groups": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
|
|
||||||
"selector-type-no-unknown": [
|
|
||||||
true,
|
|
||||||
{ "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }
|
|
||||||
],
|
|
||||||
"value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }],
|
|
||||||
"declaration-colon-newline-after": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
import { createRequire } from 'module'
|
||||||
|
import { spawn } from 'child_process'
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
const pkg = require('../package.json')
|
||||||
|
|
||||||
|
// write .debug.env
|
||||||
|
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
|
||||||
|
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
|
||||||
|
|
||||||
|
// bootstrap
|
||||||
|
spawn(
|
||||||
|
// TODO: terminate `npm run dev` when Debug exits.
|
||||||
|
process.platform === 'win32' ? 'npm.cmd' : 'npm',
|
||||||
|
['run', 'dev'],
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"dbaeumer.vscode-eslint",
|
"editorconfig.editorconfig",
|
||||||
"EditorConfig.EditorConfig",
|
"mrmlnc.vscode-json5",
|
||||||
"stylelint.vscode-stylelint",
|
"rbbit.typescript-hero",
|
||||||
"esbenp.prettier-vscode"
|
"syler.sass-indented",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,47 @@
|
|||||||
{
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Electron: Main",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"protocol": "inspector",
|
|
||||||
"runtimeExecutable": "npm",
|
|
||||||
"runtimeArgs": [
|
|
||||||
"run start:main --inspect=5858 --remote-debugging-port=9223"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "Start Webpack Dev"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Electron: Renderer",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "attach",
|
|
||||||
"port": 9223,
|
|
||||||
"webRoot": "${workspaceFolder}",
|
|
||||||
"timeout": 15000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"compounds": [
|
"compounds": [
|
||||||
{
|
{
|
||||||
"name": "Electron: All",
|
"name": "Debug App",
|
||||||
"configurations": ["Electron: Main", "Electron: Renderer"]
|
"preLaunchTask": "start .debug.script.mjs",
|
||||||
|
"configurations": [
|
||||||
|
"Debug Main Process",
|
||||||
|
"Debug Renderer Process"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"hidden": false,
|
||||||
|
"group": "",
|
||||||
|
"order": 1
|
||||||
|
},
|
||||||
|
"stopAll": true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug Main Process",
|
||||||
|
"type": "pwa-node",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||||
|
},
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--no-sandbox",
|
||||||
|
"--remote-debugging-port=9229",
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"envFile": "${workspaceFolder}/.vscode/.debug.env",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug Renderer Process",
|
||||||
|
"port": 9229,
|
||||||
|
"request": "attach",
|
||||||
|
"type": "pwa-chrome",
|
||||||
|
"timeout": 60000
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
".eslintrc": "jsonc",
|
|
||||||
".prettierrc": "jsonc",
|
|
||||||
".eslintignore": "ignore"
|
|
||||||
},
|
|
||||||
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": true,
|
|
||||||
"source.fixAll.stylelint": false
|
|
||||||
},
|
|
||||||
"css.validate": false,
|
|
||||||
"less.validate": false,
|
|
||||||
"scss.validate": false,
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,34 @@
|
|||||||
{
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"type": "npm",
|
"label": "start .debug.script.mjs",
|
||||||
"label": "Start Webpack Dev",
|
"type": "shell",
|
||||||
"script": "start:renderer",
|
"command": "node .vscode/.debug.script.mjs",
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"problemMatcher": {
|
"problemMatcher": {
|
||||||
"owner": "custom",
|
"owner": "typescript",
|
||||||
|
"fileLocation": "relative",
|
||||||
"pattern": {
|
"pattern": {
|
||||||
"regexp": "____________"
|
// TODO: correct "regexp"
|
||||||
|
"regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 3,
|
||||||
|
"column": 4,
|
||||||
|
"code": 5,
|
||||||
|
"message": 6
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"activeOnStart": true,
|
"activeOnStart": true,
|
||||||
"beginsPattern": "Compiling\\.\\.\\.$",
|
"endsPattern": "^.*[startup] Electron App.*$",
|
||||||
"endsPattern": "(Compiled successfully|Failed to compile)\\.$"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://code.visualstudio.com/docs/editor/tasks#_operating-system-specific-properties
|
||||||
|
// https://code.visualstudio.com/docs/editor/tasks#_background-watching-tasks
|
||||||
|
// https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
|
||||||
|
|||||||
@@ -1,586 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project 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).
|
|
||||||
|
|
||||||
[0.15.0] - 2022-04-13
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added setting to save and resume the current queue between sessions (#130) (Thanks @kgarner7)
|
|
||||||
- Added a simple "play random" button to the player bar (#276)
|
|
||||||
- Added new seek/volume sliders (#272)
|
|
||||||
- Seeking/dragging is now more responsive
|
|
||||||
- Added improved discord rich presence (#286)
|
|
||||||
- Added download button on the playlist view (#266)
|
|
||||||
- (Jellyfin) Added "genre" column to the artist list
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Swapped the order of "Seek Forward/Backward" and "Next/Prev Track" buttons on the player bar
|
|
||||||
- Global volume is now calculated logarithmically (#275) (Thanks @gelaechter)
|
|
||||||
- "Auto playlist" is now named "Play Random" (#276)
|
|
||||||
- "Now playing" option is now available on the "Start page" setting
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Playing songs by double clicking on a list should now play in the proper order (#279)
|
|
||||||
- (Linux) Fixed MPRIS metadata not updating when player automatically increments (#263)
|
|
||||||
- Application fonts now loaded locally instead of from Google CDN (#284)
|
|
||||||
- Enabling "Default to Album List on Artist Page" no longer performs a double redirect when entering the artist page (#271)
|
|
||||||
- Stop button is no longer disabled when playback is stopped (#273)
|
|
||||||
- Various package updates (#288) (Thanks @kgarner7)
|
|
||||||
- Top control bar show no longer be accessible when not logged in (#267)
|
|
||||||
|
|
||||||
[0.14.0] - 2022-03-12
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added zoom options via hotkeys (#252)
|
|
||||||
- Zoom in: CTRL + SHIFT + =
|
|
||||||
- Zoom out: CTRL + SHIFT + -
|
|
||||||
- Added PLAY context menu options to the Genre view (#239)
|
|
||||||
- Added STOP button to the main player controls (#252)
|
|
||||||
- Added "System Notifications" option to display native notifications when the song automatically changes (#245)
|
|
||||||
- Added arm64 build (#238)
|
|
||||||
- New languages
|
|
||||||
- Spanish (Thanks @ami-sc) (#250)
|
|
||||||
- Sinhala (Thanks @hirusha-adi) (#254)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Jellyfin) Fixed the order of returned songs when playing from the Folder view using the context menu (#240)
|
|
||||||
- (Linux) Reset MPRIS position to 0 when using "previous track" resets the song 0 (#249)
|
|
||||||
- Fixed JavaScript error when removing all songs from the queue using the context menu (#248)
|
|
||||||
- Fixed Ampache server support by adding .view to all Subsonic API endpoints (#253)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- (Windows) Removed the cover art display when hovering Sonixd on the taskbar (due to new sidebar position) (#242)
|
|
||||||
|
|
||||||
[0.13.1] - 2022-02-16
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed startup crash on all OS if the default settings file is not present (#237)
|
|
||||||
|
|
||||||
[0.13.0] - 2022-02-16
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added new searchbar and search UI (#227, #228)
|
|
||||||
- Added playback controls to the Sonixd tray menu (#225)
|
|
||||||
- Added playlist selections to the `Start Page` config option
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Sidebar changes (#206)
|
|
||||||
|
|
||||||
- Allow resizing of the sidebar when expanded
|
|
||||||
- Allow a toggle of the playerbar's cover art to the sidebar when expanded
|
|
||||||
- Display playlist list on the sidebar under the navigation
|
|
||||||
- Allow configuration of the display of sidebar elements
|
|
||||||
|
|
||||||
- Changed the `Artist` row on the playerbar to use a comma delimited list of the song's artists rather than the album artist (#218)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed the player volume not resetting to its default value when resetting a song while crossfading (#228)
|
|
||||||
- (Jellyfin) Fixed artist list not displaying user favorites
|
|
||||||
- (Jellyfin) Fixed `bitrate` column not properly by its numeric value (#220)
|
|
||||||
- Fixed javascript exception when incrementing/decrementing the queue (#230)
|
|
||||||
- Fixed popups/tooltips not using the configured font
|
|
||||||
|
|
||||||
[0.12.1] - 2022-02-02
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed translation syntax error causing application to crash when deleting playlists from the context menu (#216)
|
|
||||||
- Fixed Player behavior (#217)
|
|
||||||
- No longer scrobbles an additional time after the last song ends when repeat is off
|
|
||||||
- (Jellyfin) Properly handles scrobbling the player's pause/resume and time position
|
|
||||||
|
|
||||||
[0.12.0] - 2022-01-31
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added support for language/translations (#146) (Thanks @gelaechter)
|
|
||||||
- German translation added (Thanks @gelaechter)
|
|
||||||
- Simplified Chinese translation added (Thanks @fangxx3863)
|
|
||||||
- (Windows) Added media keys with desktop overlay (#79) (Thanks @GermanDarknes)
|
|
||||||
- (Subsonic) Added support for `/getLyrics` to display the current song's lyrics in a popup (#151)
|
|
||||||
- (Jellyfin) Added song list page
|
|
||||||
- Added config to choose the default Album/Song list sort on startup (#169)
|
|
||||||
- Added config to choose the application start page (#176) (Thanks @GermanDarknes)
|
|
||||||
- Added config for pagination for Album/Song list pages
|
|
||||||
- (Windows) Added option to set custom directory on installation (#184)
|
|
||||||
- Added config to set the default artist page to the album list (#199)
|
|
||||||
- Added info mode for the Now Playing page (#160)
|
|
||||||
- Added release notes popup
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Player behavior
|
|
||||||
- `Media Stop` now stops the track and resets it instead of clearing the queue (#200)
|
|
||||||
- `Media Prev` now resets to the start of the song if pressed after 5 seconds (#207)
|
|
||||||
- `Media Prev` now resets to the start of the song if repeat is off and is the first song of the queue (#207)
|
|
||||||
- `Media Next` now does nothing if repeat is off and is the last song of the queue (#207)
|
|
||||||
- Playing a single track in the queue without repeat no longer plays the track twice (#205)
|
|
||||||
- Scrobbling
|
|
||||||
- (Jellyfin) Scrobbling has been reverted to use the `/sessions/playing` endpoint to support the Playback Reporting plugin (#187)
|
|
||||||
- Scrobbling occurs after 5 seconds has elapsed for the current track as to not instantly mark the song as played
|
|
||||||
- Pressing `CTRL + F` or the search button now focuses the text in the searchbar (#203) (Thanks @WeekendWarrior1)
|
|
||||||
- Changed loading indicators for all pages
|
|
||||||
- OBS scrobble now outputs an image.txt file instead of the downloading the cover image (#136)
|
|
||||||
- Player Bar
|
|
||||||
- Album name now appears under the artist
|
|
||||||
- (Subsonic) 5-star rating is available
|
|
||||||
- Clicking on the cover art now displays a full-size image
|
|
||||||
- Clicking on the song name now redirects to the Now Playing queue
|
|
||||||
- (Jellyfin) Removed track limit for "Auto Playlist"
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (macOS) Fixed macOS exit behavior (#198) (Thanks @zackslash)
|
|
||||||
- (Linux) Fixed MPRIS `position` result (#162)
|
|
||||||
- (Subsonic) Fixed artist page crashing the application if server does not support `/getArtistInfo2` (#170)
|
|
||||||
- (Jellyfin) Fixed `View all songs` returning songs out of their album track order
|
|
||||||
- (Jellyfin) Fixed the "Latest Albums" on the album artist page displaying no albums
|
|
||||||
- Fixed card overlay button color on click
|
|
||||||
- Fixed buttons on the Album page to work better with light mode
|
|
||||||
- Fixed unfavorite button on Album page
|
|
||||||
|
|
||||||
[0.11.0] - 2022-01-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added external integrations
|
|
||||||
- Added Discord rich presence to display the currently playing song (#155)
|
|
||||||
- Added OBS (Open Broadcaster Software) scrobbling to send current track metadata to desktop or the Tuna plugin (#136)
|
|
||||||
- Added a `Native` option for Titlebar Style (#148) (Thanks @gelaechter)
|
|
||||||
- (Jellyfin) Added toggle to allow transcoding for non-directplay compatible filetypes (#158)
|
|
||||||
- Additional MPRIS support
|
|
||||||
- Added metadata:
|
|
||||||
- `albumArtist`, `discNumber`, `trackNumber`, `useCount`, `genre`
|
|
||||||
- Added events:
|
|
||||||
- `seek`, `position`, `volume`, `repeat`, `shuffle`
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Overhauled the Artist page
|
|
||||||
- (Jellyfin) Split albums by album artist OR compilation
|
|
||||||
- (Jellyfin) Added artist genres
|
|
||||||
- (Subsonic) Added Top Songs section
|
|
||||||
- Moved related artists to the main page scrolling menu
|
|
||||||
- Added `View All Songs` button to view all songs by the artist
|
|
||||||
- Added artist radio (mix) button
|
|
||||||
- Horizontal scrolling menu no longer displays scrollbar
|
|
||||||
- Changed button styling on Playlist/Album/Artist pages
|
|
||||||
- Changed page image styling to use the card on Playlist/Album/Artist pages
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed various MPRIS features
|
|
||||||
- Synchronized the play/pause state between the player and MPRIS client when pausing from Sonixd (#152)
|
|
||||||
- Fixed the identity of Sonixd to use the app name instead of description (#163)
|
|
||||||
- Fixed various submenus opening in the right-click context menu when the option is disabled (#164)
|
|
||||||
- Fixed compatibility with older Subsonic API servers (now targets Subsonic v1.13.0) (#144)
|
|
||||||
- Fixed playback causing heavily increased CPU/Power usage #145)
|
|
||||||
|
|
||||||
[0.10.0] - 2021-12-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added 2 new default themes
|
|
||||||
- City Lights
|
|
||||||
- One Dark
|
|
||||||
- Added additional album filters (#66)
|
|
||||||
- Genres (AND/OR)
|
|
||||||
- Artists (AND/OR)
|
|
||||||
- Years (FROM/TO)
|
|
||||||
- Added external column sort filters for multiple pages (#66)
|
|
||||||
- Added item counter to page titles
|
|
||||||
- `Play Count` column has been added to albums (only works for Navidrome)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Config page has been fully refreshed to a new look
|
|
||||||
- Config popover on the action bar now includes all config tabs
|
|
||||||
- Tooltips
|
|
||||||
- Increased default tooltip delay from 250ms -> 500ms
|
|
||||||
- Increased tooltip delay on card overlay buttons to 1000ms
|
|
||||||
- Grid view
|
|
||||||
- Placeholder images for playlists, albums, and artists have been updated (inspired from Jellyfin Web UI)
|
|
||||||
- Card title/subtitle width decreased from 100% to default length
|
|
||||||
- Separate card info section from image/overlay buttons on hover
|
|
||||||
- Popovers (config, auto playlist, etc)
|
|
||||||
- Now have decreased opacity
|
|
||||||
- Enabling/disabling global media keys no longer requires app restart
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Jellyfin) Fixed `Recently Played` and `Most Played` filters on the Dashboard page (#114)
|
|
||||||
- (Jellyfin) Fixed server scrobble (#126)
|
|
||||||
- No longer sends the `/playing` request on song start (prevents song being marked as played when it starts)
|
|
||||||
- Fixed song play count increasing multiple times per play
|
|
||||||
- (Jellyfin) Fixed tracks without embedded art displaying placeholder (#128)
|
|
||||||
- (Jellyfin) Fixed song `Path` property not displaying data
|
|
||||||
- (Subsonic) Fixed login check for Funkwhale servers (#135)
|
|
||||||
- Fixed persistent grid-view scroll position
|
|
||||||
- Fixed list-view columns
|
|
||||||
- `Visibility` column now properly displays data
|
|
||||||
- Selected media folder is now cleared from settings on disconnect (prevents errors when signing into a new server)
|
|
||||||
- Fixed adding/removing artist as favorite on the Artist page not updating
|
|
||||||
- Fixed search bar not properly handling Asian keyboard inputs
|
|
||||||
|
|
||||||
## [0.9.1] - 2021-12-07
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- List-view scroll position is now persistent for the following:
|
|
||||||
- Now Playing
|
|
||||||
- Playlist list
|
|
||||||
- Favorites (all)
|
|
||||||
- Album list
|
|
||||||
- Artist list
|
|
||||||
- Genre list
|
|
||||||
- Grid-view scroll position is now persistent for the following:
|
|
||||||
- Playlist list
|
|
||||||
- Favorites (album/artist)
|
|
||||||
- Album list
|
|
||||||
- Artist list
|
|
||||||
- (Jellyfin) Changed audio stream URL to force transcoding off (#108)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Jellyfin) Fixed the player not sending the "finish" condition when the song meets the scrobble condition (unresolved from 0.9.0) (#111)
|
|
||||||
|
|
||||||
## [0.9.0] - 2021-12-06
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added 2 new default themes
|
|
||||||
- Plex-like
|
|
||||||
- Spotify-like
|
|
||||||
- Added volume control improvements
|
|
||||||
- Volume value tooltip while hovering the slider
|
|
||||||
- Mouse scroll wheel controls volume while hovering the slider
|
|
||||||
- Clicking the volume icon will mute/unmute
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Overhauled all default themes
|
|
||||||
- Rounded buttons, inputs, etc.
|
|
||||||
- Changed grid card hover effects
|
|
||||||
- Removed hover scale
|
|
||||||
- Removed default background on overlay buttons
|
|
||||||
- Moved border to only the image instead of full card
|
|
||||||
- Album page
|
|
||||||
- Genre(s) are now listed on a line separate from the artists
|
|
||||||
- Album artist is now distinct from track artists
|
|
||||||
- Increased length of the genre/artist line from 70% -> 80%
|
|
||||||
- The genre/artist line is now scrollable using the mouse wheel
|
|
||||||
- (Jellyfin) List view
|
|
||||||
- `Artist` column now uses the album artist property
|
|
||||||
- `Title (Combined)` column now displays all track artists, comma-delimited instead of the album artist
|
|
||||||
- `Genre` column now displays all genres, comma-delimited, left-aligned
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Jellyfin) Fixed the player not sending the "finish" condition when the song meets the scrobble condition
|
|
||||||
- (Jellyfin) Fixed album lists not sorting by the `genre` column
|
|
||||||
- (Jellyfin)(API) Fixed the A-Z(Artist) not sorting by Album Artist on the album list
|
|
||||||
- (Jellyfin)(API) Fixed auto playlist not respecting the selected music folder
|
|
||||||
- (Jellyfin)(API) Fixed the artist page not respecting the selected music folder
|
|
||||||
|
|
||||||
## [0.8.5] - 2021-11-25
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed default (OOBE) title column not display data (#104)
|
|
||||||
|
|
||||||
## [0.8.4] - 2021-11-25
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Jellyfin)(Linux) Fixed JS MPRIS error when switching tracks due to unrounded song duration
|
|
||||||
- (Linux) Fixed MPRIS artist, genre, and coverart not updating on track change
|
|
||||||
|
|
||||||
## [0.8.3] - 2021-11-25
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Subsonic) Fixed playing a folder from the folder view
|
|
||||||
- Fixed rating context menu option available from the Genre page
|
|
||||||
|
|
||||||
## [0.8.2] - 2021-11-25
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added option to disable auto updates
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed gapless playback on certain \*sonic servers (#100)
|
|
||||||
- Fixed playerbar coverart not redirecting to `Now Playing` page
|
|
||||||
|
|
||||||
## [0.8.1] - 2021-11-24
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Subsonic) Fixed errors blocking playlists from being deleted
|
|
||||||
|
|
||||||
## [0.8.0] - 2021-11-24
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added Jellyfin server support (#87)
|
|
||||||
- Supports full Sonixd feature-set (except ratings)
|
|
||||||
- Added a mini config popover to change list/grid view options on the top action bar
|
|
||||||
- Added system audio device selector (#96)
|
|
||||||
- Added context menu option `Set rating` to bulk set ratings for songs (and albums/artists on Navidrome) (#95)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Reduced cached image from 500px -> 350px (to match max grid size)
|
|
||||||
- Grid/header images now respect image aspect ratio returned by the server
|
|
||||||
- Playback filter input now uses a regex validation before allowing you to add
|
|
||||||
- Renamed all `Name` columns to `Title`
|
|
||||||
- Search bar now clears after pressing enter to globally search
|
|
||||||
- Added borders to popovers
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed application performance issues when player is crossfading to the next track
|
|
||||||
- Fixed null entries showing at the beginning of descending sort on playlist/now playing lists
|
|
||||||
- Tooltips no longer pop up on the artist/playlist description when null
|
|
||||||
|
|
||||||
## [0.7.0] - 2021-11-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added download buttons on the Album and Artist pages (#29)
|
|
||||||
- Allows you to download (via browser) or copy download links to your clipboard (to use with a download manager)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Changed default tooltip delay from `500ms` -> `250ms`
|
|
||||||
- Moved search bar from page header to the main layout action bar
|
|
||||||
- Added notice for macOS media keys to require trusted accessibility in the client
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed auto playlist and album fetch in Gonic servers
|
|
||||||
- Fixed the macOS titlebar styling to better match the original (#83)
|
|
||||||
- Fixed thumbnailclip error when resizing the application in macOS (#84)
|
|
||||||
- Fixed playlist page not using cached image
|
|
||||||
|
|
||||||
## [0.6.0] - 2021-11-09
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added additional grid-view customization options (#74)
|
|
||||||
- Gap size (spaces between cards)
|
|
||||||
- Alignment (left-align, center-align)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Changed default album/artist uncached image sizes from `150px` -> `350px`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- (Windows) Fixed default taskbar thumbnail on Windows10 when minimized to use window instead of album cover (#73)
|
|
||||||
- Fixed playback settings unable to change via the UI
|
|
||||||
- Crossfade duration
|
|
||||||
- Polling interval
|
|
||||||
- Volume fade
|
|
||||||
- Fixed header styling on the Config page breaking at smaller window widths (#72)
|
|
||||||
- Fixed the position of the description tooltip on the Artist page
|
|
||||||
- Fixed the `Add to playlist` popover showing underneath the modal in modal-view
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Removed unused `fonts.size.pageTitle` theme property
|
|
||||||
|
|
||||||
## [0.5.0] - 2021-11-05
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added extensible theming (#60)
|
|
||||||
- Added playback presets (gapless, fade, normal) to the config
|
|
||||||
- Added persistence for column sort for all list-views (except playlist and search) (#47)
|
|
||||||
- Added playback filters to the config to filter out songs based on regex (#53)
|
|
||||||
- Added music folder selector in auto playlist (this may or may not work depending on your server)
|
|
||||||
- Added improved playlist, artist, and album pages
|
|
||||||
- Added dynamic images on the Playlist page for servers that don't support playlist images (e.g. Navidrome)
|
|
||||||
- Added link to open the local `settings.json` file
|
|
||||||
- Added setting to use legacy authentication (#63)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Improved overall application keyboard accessibility
|
|
||||||
- Playback no longer automatically starts if adding songs to the queue using `Add to queue`
|
|
||||||
- Prevent accidental page navigation when using [Ctrl/Shift + Click] when multi-selecting rows in list-view
|
|
||||||
- Standardized buttons between the Now Playing page and the mini player
|
|
||||||
- "Add random" renamed to "Auto playlist"
|
|
||||||
- Increased 'info' notification timeout from 1500ms -> 2000ms
|
|
||||||
- Changed default mini player columns to better fit
|
|
||||||
- Updated default themes to more modern standards (Default Dark, Default Light)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed title sort on the `Title (Combined)` column on the album list
|
|
||||||
- Fixed 2nd song in queue being skipped when using the "Play" button multiple pages (album, artist, auto playlist)
|
|
||||||
- Fixed `Title` column not showing the title on the Folder page (#69)
|
|
||||||
- Fixed context menu windows showing underneath the mini player
|
|
||||||
- Fixed `Add to queue (next)` adding songs to the wrong unshuffled index when shuffle is enabled
|
|
||||||
- Fixed local search on the root Folder page
|
|
||||||
- Fixed input picker dropdowns following the page on scroll
|
|
||||||
- Fixed the current playing song not highlighted when using `Add to queue` on an empty play queue
|
|
||||||
- Fixed artist list not using the `artistImageUrl` returned by Navidrome
|
|
||||||
|
|
||||||
## [0.4.1] - 2021-10-27
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added links to the genre column on the list-view
|
|
||||||
- Added page forward/back buttons to main layout
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Increase delay when completing mouse drag select in list view from `100ms` -> `200ms`
|
|
||||||
- Change casing for main application name `sonixd` -> `Sonixd`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed Linux media hotkey support (MPRIS)
|
|
||||||
- Added commands for additional events `play` and `pause` (used by KDE's media player overlay)
|
|
||||||
- Set status to `Playing` when initially starting a song
|
|
||||||
- Set current song metadata when track automatically changes instead of only when it manually changes
|
|
||||||
- Fixed filtered link to Album List on the Album page
|
|
||||||
- Fixed filtered link to Album List on the Dashboard page
|
|
||||||
- Fixed font color for lists/tables in panels
|
|
||||||
- Affects the search view song list and column selector list
|
|
||||||
|
|
||||||
## [0.4.0] - 2021-10-26
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added music folder selector (#52)
|
|
||||||
- Added media hotkeys / MPRIS support for Linux (#50)
|
|
||||||
- This is due to dbus overriding the global shortcuts that electron sends
|
|
||||||
- Added advanced column selector component
|
|
||||||
- Drag-n-drop list
|
|
||||||
- Individual resizable columns
|
|
||||||
- (Windows) Added tray (Thanks @ncarmic4) (#45)
|
|
||||||
- Settings to minimize/exit to tray
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Page selections are now persistent
|
|
||||||
- Active tab on config page
|
|
||||||
- Active tab on favorites page
|
|
||||||
- Filter selector on album list page
|
|
||||||
- Playlists can now be saved after being sorted using column filters
|
|
||||||
- Folder view
|
|
||||||
- Now shows all root folders in the list instead of in the input picker
|
|
||||||
- Now shows music folders in the input picker
|
|
||||||
- Now uses loader when switching pages
|
|
||||||
- Changed styling for various views/components
|
|
||||||
- Look & Feel setting page now split up into multiple panels
|
|
||||||
- Renamed context menu button `Remove from current` -> `Remove selected`
|
|
||||||
- Page header titles width increased from `45%` -> `80%`
|
|
||||||
- Renamed `Scan library` -> `Scan`
|
|
||||||
- All pages no longer refetch data when clicking back into the application
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed shift-click multi select on a column-sorted list-view
|
|
||||||
- Fixed right-click context menu showing up behind all modals (#55)
|
|
||||||
- Fixed mini player showing up behind tag picker elements
|
|
||||||
- Fixed duration showing up as `NaN:NaN` when duration is null or invalid
|
|
||||||
- Fixed albums showing as a folder in Navidrome instances
|
|
||||||
|
|
||||||
## [0.3.0] - 2021-10-16
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added folder browser (#1)
|
|
||||||
- Added context menu button `View in folder`
|
|
||||||
- Requires that your server has support for the original `/getIndexes` and `/getMusicDirectory` endpoints
|
|
||||||
- Added configurable row-hover highlight for list-view
|
|
||||||
- (Windows) Added playback controls in thumbnail toolbar (#32)
|
|
||||||
- (Windows/macOS) Added window size/position remembering on application close (#31)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Changed styling for various views/components
|
|
||||||
- Tooltips added on grid-view card hover buttons
|
|
||||||
- Mini-player removed rounded borders and increased opacity
|
|
||||||
- Mini-player removed animation on open/close
|
|
||||||
- Search bar now activated from button -> input on click / CTRL+F
|
|
||||||
- Page header toolbar buttons styling consistency
|
|
||||||
- Album list filter moved from right -> left
|
|
||||||
- Reordered context menu button `Move selected to [...]`
|
|
||||||
- Decreased horizontal width of expanded sidebar from 193px -> 165px
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed duplicate scrobble requests when pause/resuming a song after the scrobble threshold (#30)
|
|
||||||
- Fixed genre column not applying in the song list-view
|
|
||||||
- Fixed default titlebar set on first run
|
|
||||||
|
|
||||||
## [0.2.1] - 2021-10-11
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed using play buttons on the artist view not starting playback
|
|
||||||
- Fixed favoriting on horizontal scroll menu on dashboard/search views
|
|
||||||
- Fixed typo on default artist list viewtype
|
|
||||||
- Fixed artist image selection on artist view
|
|
||||||
|
|
||||||
## [0.2.0] - 2021-10-11
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added setting to enable scrobbling playing/played tracks to your server (#17)
|
|
||||||
- Added setting to change between macOS and Windows styled titlebar (#23)
|
|
||||||
- Added app/build versions and update checker on the config page (#18)
|
|
||||||
- Added 'view in modal' button on the list-view context menu (#8)
|
|
||||||
- Added a persistent indicator on grid-view cards for favorited albums/artists (#7)
|
|
||||||
- Added buttons for 'Add to queue (next)' and 'Add to queue (later)' (#6)
|
|
||||||
- Added left/right scroll buttons to the horizontal scrolling menu (dashboard/search)
|
|
||||||
- Added last.fm link to artist page
|
|
||||||
- Added link to cache location to open in local file explorer
|
|
||||||
- Added reset to default for cache location
|
|
||||||
- Added additional tooltips
|
|
||||||
- Grid-view card title and subtitle buttons
|
|
||||||
- Cover art on the player bar
|
|
||||||
- Header titles on album/artist pages
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Changed starring logic on grid-view card to update local cache instead of refetch
|
|
||||||
- Changed styling for various views/components
|
|
||||||
- Use dynamically sized hover buttons on grid-view cards depending on the card size
|
|
||||||
- Decreased size of buttons on album/playlist/artist pages
|
|
||||||
- Input picker text color changed from primary theme color to primary text color
|
|
||||||
- Crossfade type config changed from radio buttons to input picker
|
|
||||||
- Disconnect button color from red to default
|
|
||||||
- Tooltip styling updated to better match default theme
|
|
||||||
- Changed tag links to text links on album page
|
|
||||||
- Changed page header images to use cache (album/artist)
|
|
||||||
- Artist image now falls back to last.fm if no local image
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed song & image caching (#16)
|
|
||||||
- Fixed set default artist list view type on first startup
|
|
||||||
|
|
||||||
## [0.1.0] - 2021-10-06
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Initial release
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Stage 1 - Build frontend
|
|
||||||
FROM node:16.5-alpine as ui-builder
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . .
|
|
||||||
RUN npm install && npm run build:renderer
|
|
||||||
|
|
||||||
# Stage 2 - Build server
|
|
||||||
FROM node:16.5-alpine as server-builder
|
|
||||||
WORKDIR /app
|
|
||||||
COPY src/server .
|
|
||||||
RUN ls -lh
|
|
||||||
RUN npm install
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Stage 3 - Deploy
|
|
||||||
FROM node:16.5-alpine
|
|
||||||
WORKDIR /root
|
|
||||||
RUN mkdir appdata
|
|
||||||
RUN mkdir sonixd-server
|
|
||||||
RUN mkdir sonixd-client
|
|
||||||
|
|
||||||
# Install server modules
|
|
||||||
COPY src/server/package.json ./sonixd-server
|
|
||||||
RUN cd ./sonixd-server && npm install --production
|
|
||||||
|
|
||||||
# Add server build files
|
|
||||||
COPY --from=server-builder /app/dist ./sonixd-server
|
|
||||||
COPY --from=server-builder /app/prisma ./sonixd-server/prisma
|
|
||||||
|
|
||||||
# Add client build files
|
|
||||||
COPY --from=ui-builder /app/release/app/dist/renderer ./sonixd-client
|
|
||||||
|
|
||||||
COPY docker-entrypoint.sh ./sonixd-server/docker-entrypoint.sh
|
|
||||||
RUN chmod +x ./sonixd-server/docker-entrypoint.sh
|
|
||||||
|
|
||||||
RUN cd ./sonixd-server && npx prisma generate
|
|
||||||
RUN npm install pm2 -g
|
|
||||||
|
|
||||||
WORKDIR /root/sonixd-server
|
|
||||||
|
|
||||||
EXPOSE 9321
|
|
||||||
CMD ["sh", "docker-entrypoint.sh"]
|
|
||||||
@@ -1,129 +1,15 @@
|
|||||||
<img src="assets/icon.png" alt="sonixd logo" title="sonixd" align="right" height="60px" />
|
# Sonixd (rewrite)
|
||||||
|
|
||||||
# Sonixd
|
Repository for the rewrite of [Sonixd](https://github.com/jeffvli/sonixd).
|
||||||
|
|
||||||
<a href="https://github.com/jeffvli/sonixd/releases">
|
## Development
|
||||||
<img src="https://img.shields.io/github/v/release/jeffvli/sonixd?style=flat-square&color=blue"
|
|
||||||
alt="Release">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/jeffvli/sonixd/blob/main/LICENSE">
|
|
||||||
<img src="https://img.shields.io/github/license/jeffvli/sonixd?style=flat-square&color=brightgreen"
|
|
||||||
alt="License">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/jeffvli/sonixd/releases">
|
|
||||||
<img src="https://img.shields.io/github/downloads/jeffvli/sonixd/total?style=flat-square&color=orange"
|
|
||||||
alt="Downloads">
|
|
||||||
</a>
|
|
||||||
<a href="https://discord.gg/FVKpcMDy5f">
|
|
||||||
<img src="https://img.shields.io/discord/922656312888811530?color=red&label=discord&logo=discord&logoColor=white"
|
|
||||||
alt="Discord">
|
|
||||||
</a>
|
|
||||||
<a href="https://matrix.to/#/#sonixd:matrix.org">
|
|
||||||
<img src="https://img.shields.io/matrix/sonixd:matrix.org?color=red&label=matrix&logo=matrix&logoColor=white"
|
|
||||||
alt="Matrix">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
Sonixd is a cross-platform desktop client built for Subsonic-API (and Jellyfin in 0.8.0+) compatible music servers. This project was inspired by the many existing clients, but aimed to address a few key issues including <strong>scalability</strong>, <strong>library management</strong>, and <strong>user experience</strong>.
|
TBD
|
||||||
|
|
||||||
- [**Usage documentation & FAQ**](https://github.com/jeffvli/sonixd/discussions/15)
|
### Developing with Docker Compose
|
||||||
- [**Theming documentation**](https://github.com/jeffvli/sonixd/discussions/61)
|
|
||||||
|
|
||||||
Sonixd has been tested on the following: [Navidrome](https://github.com/navidrome/navidrome), [Airsonic](https://github.com/airsonic/airsonic), [Airsonic-Advanced](https://github.com/airsonic-advanced/airsonic-advanced), [Gonic](https://github.com/sentriz/gonic), [Astiga](https://asti.ga/), [Jellyfin](https://github.com/jellyfin/jellyfin)
|
TBD
|
||||||
|
|
||||||
### [Demo Sonixd using Navidrome](https://github.com/jeffvli/sonixd/discussions/244)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- HTML5 audio with crossfading and gapless\* playback
|
|
||||||
- Drag and drop rows with multi-select
|
|
||||||
- Modify and save playlists intuitively
|
|
||||||
- Handles large playlists and queues
|
|
||||||
- Global mediakeys (and partial MPRIS) support
|
|
||||||
- Multi-theme support
|
|
||||||
- Supports all Subsonic/Jellyfin API compatible servers
|
|
||||||
- Built with Electron, React with the [rsuite v4](https://github.com/rsuite/rsuite) component library
|
|
||||||
|
|
||||||
<h5>* Gapless playback is artifically created using the crossfading players so it may not be perfect, YMMV.</h5>
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
<a href="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/album.png"><img src="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/album.png" width="49.5%"/></a>
|
|
||||||
<a href="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/artist.png"><img src="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/artist.png" width="49.5%"/></a>
|
|
||||||
<a href="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/search.png"><img src="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/search.png" width="49.5%"/></a>
|
|
||||||
<a href="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/now_playing.png"><img src="https://raw.githubusercontent.com/jeffvli/sonixd/main/assets/screenshots/0.13.1/now_playing.png" width="49.5%"/></a>
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
You can install sonixd by downloading the [latest release](https://github.com/jeffvli/sonixd/releases) for your specified operating system.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
If you prefer not to download the release binary, you can install using `winget`.
|
|
||||||
|
|
||||||
Using your favorite terminal (cmd/pwsh):
|
|
||||||
|
|
||||||
```
|
|
||||||
winget install sonixd
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Arch Linux
|
|
||||||
|
|
||||||
There is an AUR package of the latest AppImage release available [here](https://aur.archlinux.org/packages/sonixd-appimage).
|
|
||||||
|
|
||||||
To install it you can use your favourite AUR package manager and install the package: `sonixd-appimage`
|
|
||||||
|
|
||||||
For example using `yay`:
|
|
||||||
|
|
||||||
```
|
|
||||||
yay -S sonixd-appimage
|
|
||||||
```
|
|
||||||
|
|
||||||
If you encounter any problems please comment on the [AUR](https://aur.archlinux.org/packages/sonixd-appimage) or contact the [maintainer](mailto:robin@blckct.io) directly before you open an issue here.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Once installed, run the application and sign in to your music server with the following details. If you are using [airsonic-advanced](https://github.com/airsonic-advanced/airsonic-advanced), you will need to make sure that you create a `decodable` credential for your login user within the admin control panel.
|
|
||||||
|
|
||||||
- Server - `e.g. http://localhost:4040/`
|
|
||||||
- User name - `e.g. admin`
|
|
||||||
- Password - `e.g. supersecret!`
|
|
||||||
|
|
||||||
If you have any questions, feel free to check out the [Usage Documentation & FAQ](https://github.com/jeffvli/sonixd/discussions/15).
|
|
||||||
|
|
||||||
## Development / Contributing
|
|
||||||
|
|
||||||
This project is built off of [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate) v2.3.0.
|
|
||||||
If you want to contribute to this project, please first create an [issue](https://github.com/jeffvli/sonixd/issues/new) or [discussion](https://github.com/jeffvli/sonixd/discussions/new) so that we can both discuss the idea and its feasability for integration.
|
|
||||||
|
|
||||||
First, clone the repo via git and install dependencies (Windows development now requires additional setup, see [#232](https://github.com/jeffvli/sonixd/issues/232)):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/jeffvli/sonixd.git
|
|
||||||
yarn install
|
|
||||||
```
|
|
||||||
|
|
||||||
Start the app in the `dev` environment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn start
|
|
||||||
```
|
|
||||||
|
|
||||||
To package apps for the local platform:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn package
|
|
||||||
```
|
|
||||||
|
|
||||||
If you receive errors while packaging the application, try upgrading/downgrading your Node version (tested on v14.18.0).
|
|
||||||
|
|
||||||
If you are unable to run via debug in VS Code, check troubleshooting steps [here](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/2757#issuecomment-784200527).
|
|
||||||
|
|
||||||
If your devtools extensions are failing to run/install, check troubleshooting steps [here](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/2788).
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[GNU General Public License v3.0 ©](https://github.com/jeffvli/sonixd/blob/main/LICENSE)
|
[GNU General Public License v3.0 ©](https://github.com/jeffvli/sonixd-rewrite/blob/dev/LICENSE)
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
type Styles = Record<string, string>;
|
|
||||||
|
|
||||||
declare module '*.svg' {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?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>
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,47 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
db:
|
|
||||||
container_name: sonixd_db
|
|
||||||
image: postgres:13
|
|
||||||
volumes:
|
|
||||||
- ${DATABASE_PERSIST_PATH}:/var/lib/postgresql/data
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=${DATABASE_USERNAME}
|
|
||||||
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
|
|
||||||
- POSTGRES_DB=${DATABASE_NAME}
|
|
||||||
ports:
|
|
||||||
- '${DATABASE_PORT}:5432'
|
|
||||||
restart: unless-stopped
|
|
||||||
server:
|
|
||||||
container_name: sonixd_server
|
|
||||||
volumes:
|
|
||||||
- ./src/server:/app # Synchronise docker container with local change
|
|
||||||
- /app/node_modules # Avoid re-copying local node_modules. Cache in container.
|
|
||||||
build:
|
|
||||||
context: ./src/server
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
environment:
|
|
||||||
- APP_BASE_URL=${APP_BASE_URL}
|
|
||||||
- DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@db/${DATABASE_NAME}?schema=public&connection_limit=14&pool_timeout=20
|
|
||||||
- DATABASE_PORT=${DATABASE_PORT}
|
|
||||||
- TOKEN_SECRET=${TOKEN_SECRET}
|
|
||||||
ports:
|
|
||||||
- '9321:9321'
|
|
||||||
restart: unless-stopped
|
|
||||||
prisma:
|
|
||||||
container_name: sonixd_prisma_studio
|
|
||||||
volumes:
|
|
||||||
- ./src/server/prisma:/app/prisma
|
|
||||||
build:
|
|
||||||
context: ./src/server/prisma
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
- server
|
|
||||||
environment:
|
|
||||||
- DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@db/${DATABASE_NAME}?schema=public
|
|
||||||
ports:
|
|
||||||
- '5555:5555'
|
|
||||||
restart: unless-stopped
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
db:
|
|
||||||
container_name: sonixd_db
|
|
||||||
image: postgres:13
|
|
||||||
ports:
|
|
||||||
- '5432:5432'
|
|
||||||
volumes:
|
|
||||||
- ${DB_PERSIST_PATH}:/var/lib/postgresql/data
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=${DB_USERNAME}
|
|
||||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
||||||
- POSTGRES_DB=${DB_NAME}
|
|
||||||
server:
|
|
||||||
container_name: sonixd
|
|
||||||
image: sonixd:latest
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
environment:
|
|
||||||
- APP_BASE_URL=${APP_BASE_URL}
|
|
||||||
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@db/${DB_NAME}?schema=public&connection_limit=14&pool_timeout=20
|
|
||||||
- DATABASE_SECRET=${DB_SECRET}
|
|
||||||
ports:
|
|
||||||
- '9321:9321'
|
|
||||||
restart: always
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
npx prisma migrate deploy
|
|
||||||
npx ts-node prisma/seed.ts
|
|
||||||
pm2-runtime server.js
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"appId": "TEST",
|
||||||
|
"productName": "TEST",
|
||||||
|
"copyright": "Copyright © 2022 ${author}",
|
||||||
|
"directories": {
|
||||||
|
"app": "release/app",
|
||||||
|
"output": "release/build",
|
||||||
|
"buildResources": "electron/resources"
|
||||||
|
},
|
||||||
|
"extends": null,
|
||||||
|
"asar": true,
|
||||||
|
"asarUnpack": ["**\\*.{node,dll}", "prisma"],
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"package.json",
|
||||||
|
"prisma/**/*",
|
||||||
|
"resources/**/*",
|
||||||
|
"!**/node_modules/@prisma/engines/introspection-engine*",
|
||||||
|
"!**/node_modules/@prisma/engines/migration-engine*",
|
||||||
|
"!**/node_modules/@prisma/engines/prisma-fmt*",
|
||||||
|
"!**/node_modules/@prisma/engines/query_engine-*",
|
||||||
|
"!**/node_modules/@prisma/engines/libquery_engine*",
|
||||||
|
"!**/node_modules/prisma/query_engine*",
|
||||||
|
"!**/node_modules/prisma/libquery_engine*",
|
||||||
|
"!**/node_modules/prisma/**/*.mjs"
|
||||||
|
],
|
||||||
|
"win": {
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "nsis",
|
||||||
|
"arch": ["x64"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"artifactName": "${productName}-Windows-${version}-Setup.${ext}"
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": false,
|
||||||
|
"perMachine": false,
|
||||||
|
"allowToChangeInstallationDirectory": true,
|
||||||
|
"deleteAppDataOnUninstall": false
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"target": ["dmg"],
|
||||||
|
"artifactName": "${productName}-Mac-${version}-Installer.${ext}"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"icon": "electron/resources/iconset",
|
||||||
|
"target": ["AppImage", "deb"],
|
||||||
|
"artifactName": "${productName}-Linux-${version}.${ext}"
|
||||||
|
},
|
||||||
|
"extraResources": [
|
||||||
|
"./assets/**",
|
||||||
|
"prisma/**/*",
|
||||||
|
"node_modules/@prisma/engines/migration-engine*",
|
||||||
|
"node_modules/@prisma/engines/query*",
|
||||||
|
"node_modules/@prisma/engines/libquery*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/// <reference types="vite-electron-plugin/electron-env" />
|
||||||
|
|
||||||
|
declare namespace NodeJS {
|
||||||
|
interface ProcessEnv {
|
||||||
|
DIST: string;
|
||||||
|
DIST_ELECTRON: string;
|
||||||
|
/** /dist/ or /public/ */
|
||||||
|
PUBLIC: string;
|
||||||
|
VSCODE_DEBUG?: 'true';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { app, ipcMain } from 'electron';
|
||||||
|
import isDev from 'electron-is-dev';
|
||||||
|
import './server';
|
||||||
|
|
||||||
|
const dbPath = isDev
|
||||||
|
? path.join(__dirname, '../../../../prisma/dev.db')
|
||||||
|
: path.join(app.getPath('userData'), 'database.db');
|
||||||
|
|
||||||
|
if (!isDev) {
|
||||||
|
try {
|
||||||
|
// database file does not exist, need to create
|
||||||
|
fs.copyFileSync(path.join(process.resourcesPath, 'prisma/dev.db'), dbPath, fs.constants.COPYFILE_EXCL);
|
||||||
|
console.log(`DB does not exist. Create new DB from ${path.join(process.resourcesPath, 'prisma/dev.db')}`);
|
||||||
|
} catch (err) {
|
||||||
|
if (err && 'code' in (err as { code: string }) && (err as { code: string }).code !== 'EEXIST') {
|
||||||
|
console.error(`DB creation faild. Reason:`, err);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlatformName(): string {
|
||||||
|
const isDarwin = process.platform === 'darwin';
|
||||||
|
if (isDarwin && process.arch === 'arm64') {
|
||||||
|
return `${process.platform}Arm64`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformToExecutables: Record<string, any> = {
|
||||||
|
darwin: {
|
||||||
|
migrationEngine: 'node_modules/@prisma/engines/migration-engine-darwin',
|
||||||
|
queryEngine: 'node_modules/@prisma/engines/libquery_engine-darwin.dylib.node',
|
||||||
|
},
|
||||||
|
darwinArm64: {
|
||||||
|
migrationEngine: 'node_modules/@prisma/engines/migration-engine-darwin-arm64',
|
||||||
|
queryEngine: 'node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node',
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
migrationEngine: 'node_modules/@prisma/engines/migration-engine-debian-openssl-1.1.x',
|
||||||
|
queryEngine: 'node_modules/@prisma/engines/libquery_engine-debian-openssl-1.1.x.so.node',
|
||||||
|
},
|
||||||
|
win32: {
|
||||||
|
migrationEngine: 'node_modules/@prisma/engines/migration-engine-windows.exe',
|
||||||
|
queryEngine: 'node_modules/@prisma/engines/query_engine-windows.dll.node',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const extraResourcesPath = app.getAppPath().replace('app.asar', ''); // impacted by extraResources setting in electron-builder.yml
|
||||||
|
const platformName = getPlatformName();
|
||||||
|
|
||||||
|
const mePath = path.join(extraResourcesPath, platformToExecutables[platformName].migrationEngine);
|
||||||
|
const qePath = path.join(extraResourcesPath, platformToExecutables[platformName].queryEngine);
|
||||||
|
|
||||||
|
ipcMain.on('config:get-app-path', (event) => {
|
||||||
|
event.returnValue = app.getAppPath();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('config:get-platform-name', (event) => {
|
||||||
|
const isDarwin = process.platform === 'darwin';
|
||||||
|
event.returnValue =
|
||||||
|
isDarwin && process.arch === 'arm64' ? `${process.platform}Arm64` : (event.returnValue = process.platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('config:get-prisma-db-path', (event) => {
|
||||||
|
event.returnValue = dbPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('config:get-prisma-me-path', (event) => {
|
||||||
|
event.returnValue = mePath;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('config:get-prisma-qe-path', (event) => {
|
||||||
|
event.returnValue = qePath;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const prisma = new PrismaClient({
|
||||||
|
datasources: {
|
||||||
|
db: {
|
||||||
|
url: `file:${dbPath}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorFormat: 'minimal',
|
||||||
|
// see https://github.com/prisma/prisma/discussions/5200
|
||||||
|
// __internal: {
|
||||||
|
// engine: {
|
||||||
|
// binaryPath: qePath,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
|
||||||
|
prisma.server.findMany({
|
||||||
|
where: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const exclude = <T, Key extends keyof T>(resultSet: T, ...keys: Key[]): Omit<T, Key> => {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const key of keys) {
|
||||||
|
delete resultSet[key];
|
||||||
|
}
|
||||||
|
return resultSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
function sleep(ms: number) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
prisma.$use(async (params, next) => {
|
||||||
|
const maxRetries = 5;
|
||||||
|
let retries = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
const result = await next(params);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
retries += 1;
|
||||||
|
return sleep(500);
|
||||||
|
}
|
||||||
|
} while (retries < maxRetries);
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
import { prisma } from '..';
|
||||||
|
|
||||||
|
export enum ServerApi {
|
||||||
|
GET_SERVER = 'api:server:get-server',
|
||||||
|
GET_SERVERS = 'api:server:get-servers',
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(ServerApi.GET_SERVERS, async () => {
|
||||||
|
const result = await prisma.server.findMany();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import './mpv-player';
|
||||||
|
import './api';
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import MpvAPI from 'node-mpv';
|
import MpvAPI from 'node-mpv';
|
||||||
import { PlayerData } from '../../../../renderer/store';
|
import { getWindow } from '../../..';
|
||||||
import { getMainWindow } from '../../../main';
|
|
||||||
|
|
||||||
const mpv = new MpvAPI(
|
const mpv = new MpvAPI(
|
||||||
{
|
{
|
||||||
@@ -20,24 +19,24 @@ mpv.start().catch((error: any) => {
|
|||||||
mpv.on('status', (status: any) => {
|
mpv.on('status', (status: any) => {
|
||||||
if (status.property === 'playlist-pos') {
|
if (status.property === 'playlist-pos') {
|
||||||
if (status.value !== 0) {
|
if (status.value !== 0) {
|
||||||
getMainWindow()?.webContents.send('renderer-player-set-queue-next');
|
getWindow()?.webContents.send('renderer-player-auto-next');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Automatically updates the play button when the player is playing
|
// Automatically updates the play button when the player is playing
|
||||||
mpv.on('started', () => {
|
mpv.on('started', () => {
|
||||||
getMainWindow()?.webContents.send('renderer-player-play');
|
getWindow()?.webContents.send('renderer-player-play');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Automatically updates the play button when the player is stopped
|
// Automatically updates the play button when the player is stopped
|
||||||
mpv.on('stopped', () => {
|
mpv.on('stopped', () => {
|
||||||
getMainWindow()?.webContents.send('renderer-player-stop');
|
getWindow()?.webContents.send('renderer-player-stop');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Automatically updates the play button when the player is paused
|
// Automatically updates the play button when the player is paused
|
||||||
mpv.on('paused', () => {
|
mpv.on('paused', () => {
|
||||||
getMainWindow()?.webContents.send('renderer-player-pause');
|
getWindow()?.webContents.send('renderer-player-pause');
|
||||||
});
|
});
|
||||||
|
|
||||||
mpv.on('quit', () => {
|
mpv.on('quit', () => {
|
||||||
@@ -46,7 +45,7 @@ mpv.on('quit', () => {
|
|||||||
|
|
||||||
// Event output every interval set by time_update, used to update the current time
|
// Event output every interval set by time_update, used to update the current time
|
||||||
mpv.on('timeposition', (time: number) => {
|
mpv.on('timeposition', (time: number) => {
|
||||||
getMainWindow()?.webContents.send('renderer-player-current-time', time);
|
getWindow()?.webContents.send('renderer-player-current-time', time);
|
||||||
});
|
});
|
||||||
|
|
||||||
mpv.on('seek', () => {
|
mpv.on('seek', () => {
|
||||||
@@ -75,7 +74,7 @@ ipcMain.on('player-next', async () => {
|
|||||||
|
|
||||||
// Stops the player
|
// Stops the player
|
||||||
ipcMain.on('player-previous', async () => {
|
ipcMain.on('player-previous', async () => {
|
||||||
await mpv.previous();
|
await mpv.prev();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Seeks forward or backward by the given amount of seconds
|
// Seeks forward or backward by the given amount of seconds
|
||||||
@@ -88,8 +87,8 @@ ipcMain.on('player-seek-to', async (_event, time: number) => {
|
|||||||
await mpv.goToPosition(time);
|
await mpv.goToPosition(time);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sets the queue to the given data. Used when manually starting a song or using the next/prev buttons
|
// Sets the queue in position 0 and 1 to the given data. Used when manually starting a song or using the next/prev buttons
|
||||||
ipcMain.on('player-set-queue', async (_event, data: PlayerData) => {
|
ipcMain.on('player-set-queue', async (_event, data: any) => {
|
||||||
if (data.queue.current) {
|
if (data.queue.current) {
|
||||||
await mpv.load(data.queue.current.streamUrl, 'replace');
|
await mpv.load(data.queue.current.streamUrl, 'replace');
|
||||||
}
|
}
|
||||||
@@ -99,8 +98,26 @@ ipcMain.on('player-set-queue', async (_event, data: PlayerData) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Replaces the queue in position 1 to the given data
|
||||||
|
ipcMain.on('player-set-queue-next', async (_event, data: any) => {
|
||||||
|
const size = await mpv.getPlaylistSize();
|
||||||
|
|
||||||
|
if (size > 1) {
|
||||||
|
await mpv.playlistRemove(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.queue.next) {
|
||||||
|
await mpv.load(data.queue.next.streamUrl, 'append');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Sets the next song in the queue when reaching the end of the queue
|
// Sets the next song in the queue when reaching the end of the queue
|
||||||
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
|
ipcMain.on('player-auto-next', async (_event, data: any) => {
|
||||||
|
// Always keep the current song as position 0 in the mpv queue
|
||||||
|
// This allows us to easily set update the next song in the queue without
|
||||||
|
// disturbing the currently playing song
|
||||||
|
await mpv.playlistRemove(0);
|
||||||
|
|
||||||
if (data.queue.next) {
|
if (data.queue.next) {
|
||||||
await mpv.load(data.queue.next.streamUrl, 'append');
|
await mpv.load(data.queue.next.streamUrl, 'append');
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import './core';
|
||||||
|
|
||||||
|
require(`./${process.platform}`);
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
// The built directory structure
|
||||||
|
//
|
||||||
|
// ├─┬ dist-electron
|
||||||
|
// │ ├─┬ main
|
||||||
|
// │ │ └── index.js > Electron-Main
|
||||||
|
// │ └─┬ preload
|
||||||
|
// │ └── index.js > Preload-Scripts
|
||||||
|
// ├─┬ dist
|
||||||
|
// │ └── index.html > Electron-Renderer
|
||||||
|
//
|
||||||
|
process.env.DIST_ELECTRON = join(__dirname, '..');
|
||||||
|
process.env.DIST = join(process.env.DIST_ELECTRON, '../dist');
|
||||||
|
process.env.PUBLIC = app.isPackaged ? process.env.DIST : join(process.env.DIST_ELECTRON, '../public');
|
||||||
|
|
||||||
|
import { release } from 'os';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { app, BrowserWindow, shell, ipcMain } from 'electron';
|
||||||
|
import './features';
|
||||||
|
|
||||||
|
// Disable GPU Acceleration for Windows 7
|
||||||
|
if (release().startsWith('6.1')) app.disableHardwareAcceleration();
|
||||||
|
|
||||||
|
// Set application name for Windows 10+ notifications
|
||||||
|
if (process.platform === 'win32') app.setAppUserModelId(app.getName());
|
||||||
|
|
||||||
|
if (!app.requestSingleInstanceLock()) {
|
||||||
|
app.quit();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let win: BrowserWindow | null = null;
|
||||||
|
// Here, you can also use other preload
|
||||||
|
const preload = join(__dirname, '../preload/index.js');
|
||||||
|
const url = process.env.VITE_DEV_SERVER_URL as string;
|
||||||
|
const indexHtml = join(process.env.DIST, 'index.html');
|
||||||
|
|
||||||
|
async function createWindow() {
|
||||||
|
win = new BrowserWindow({
|
||||||
|
icon: join(process.env.PUBLIC as string, 'favicon.svg'),
|
||||||
|
title: 'Main window',
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
preload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (app.isPackaged) {
|
||||||
|
win.loadFile(indexHtml);
|
||||||
|
} else {
|
||||||
|
win.loadURL(url);
|
||||||
|
// win.webContents.openDevTools()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test actively push message to the Electron-Renderer
|
||||||
|
win.webContents.on('did-finish-load', () => {
|
||||||
|
win?.webContents.send('main-process-message', new Date().toLocaleString());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make all links open with the browser, not with the application
|
||||||
|
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
|
if (url.startsWith('https:')) shell.openExternal(url);
|
||||||
|
return { action: 'deny' };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(createWindow);
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
win = null;
|
||||||
|
if (process.platform !== 'darwin') app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('second-instance', () => {
|
||||||
|
if (win) {
|
||||||
|
// Focus on the main window if the user tried to open another
|
||||||
|
if (win.isMinimized()) win.restore();
|
||||||
|
win.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
const allWindows = BrowserWindow.getAllWindows();
|
||||||
|
if (allWindows.length) {
|
||||||
|
allWindows[0].focus();
|
||||||
|
} else {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// new window example arg: new windows url
|
||||||
|
ipcMain.handle('open-win', (event, arg) => {
|
||||||
|
const childWindow = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
preload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (app.isPackaged) {
|
||||||
|
childWindow.loadFile(indexHtml, { hash: arg });
|
||||||
|
} else {
|
||||||
|
childWindow.loadURL(`${url}/#${arg}`);
|
||||||
|
// childWindow.webContents.openDevTools({ mode: "undocked", activate: true })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getWindow = () => {
|
||||||
|
return win;
|
||||||
|
};
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (condition.includes(document.readyState)) {
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
document.addEventListener('readystatechange', () => {
|
||||||
|
if (condition.includes(document.readyState)) {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeDOM = {
|
||||||
|
append(parent: HTMLElement, child: HTMLElement) {
|
||||||
|
if (!Array.from(parent.children).find((e) => e === child)) {
|
||||||
|
return parent.appendChild(child);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove(parent: HTMLElement, child: HTMLElement) {
|
||||||
|
if (Array.from(parent.children).find((e) => e === child)) {
|
||||||
|
return parent.removeChild(child);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://tobiasahlin.com/spinkit
|
||||||
|
* https://connoratherton.com/loaders
|
||||||
|
* https://projects.lukehaas.me/css-loaders
|
||||||
|
* https://matejkustec.github.io/SpinThatShit
|
||||||
|
*/
|
||||||
|
function useLoading() {
|
||||||
|
const className = `loaders-css__square-spin`;
|
||||||
|
const styleContent = `
|
||||||
|
@keyframes square-spin {
|
||||||
|
25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
|
||||||
|
50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
|
||||||
|
75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
|
||||||
|
100% { transform: perspective(100px) rotateX(0) rotateY(0); }
|
||||||
|
}
|
||||||
|
.${className} > div {
|
||||||
|
animation-fill-mode: both;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
background: #fff;
|
||||||
|
animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
|
||||||
|
}
|
||||||
|
.app-loading-wrap {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #282c34;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const oStyle = document.createElement('style');
|
||||||
|
const oDiv = document.createElement('div');
|
||||||
|
|
||||||
|
oStyle.id = 'app-loading-style';
|
||||||
|
oStyle.innerHTML = styleContent;
|
||||||
|
oDiv.className = 'app-loading-wrap';
|
||||||
|
oDiv.innerHTML = `<div class="${className}"><div></div></div>`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
appendLoading() {
|
||||||
|
safeDOM.append(document.head, oStyle);
|
||||||
|
safeDOM.append(document.body, oDiv);
|
||||||
|
},
|
||||||
|
removeLoading() {
|
||||||
|
safeDOM.remove(document.head, oStyle);
|
||||||
|
safeDOM.remove(document.body, oDiv);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
const { appendLoading, removeLoading } = useLoading();
|
||||||
|
domReady().then(appendLoading);
|
||||||
|
|
||||||
|
window.onmessage = (ev) => {
|
||||||
|
ev.data.payload === 'removeLoading' && removeLoading();
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(removeLoading, 4999);
|
||||||
|
|
||||||
|
const serverApi = {
|
||||||
|
getServer: () => ipcRenderer.invoke('api:server:get-server'), // ServerApi.GET_SERVER
|
||||||
|
getServers: () => ipcRenderer.invoke('api:server:get-servers'), // ServerApi.GET_SERVERS
|
||||||
|
};
|
||||||
|
|
||||||
|
const api = {
|
||||||
|
prisma: {
|
||||||
|
server: serverApi,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('electron', api);
|
||||||
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||||
|
<title>Vite App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,309 +1,86 @@
|
|||||||
{
|
{
|
||||||
"name": "sonixd",
|
"name": "sonixd-rewrite",
|
||||||
"productName": "Sonixd",
|
"productName": "sonixd-rewrite",
|
||||||
"description": "A full-featured Subsonic/Jellyfin compatible music player",
|
"private": true,
|
||||||
"scripts": {
|
"version": "0.0.0",
|
||||||
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
|
"description": "",
|
||||||
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
|
"author": "jeffvli",
|
||||||
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
|
|
||||||
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
|
|
||||||
"lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
|
|
||||||
"lint:styles": "npx stylelint **/*.tsx",
|
|
||||||
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never",
|
|
||||||
"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",
|
|
||||||
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
|
|
||||||
"start:main": "cross-env NODE_ENV=development electron -r ts-node/register/transpile-only ./src/main/main.ts",
|
|
||||||
"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",
|
|
||||||
"start:web": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.web.ts",
|
|
||||||
"test": "jest",
|
|
||||||
"prepare": "husky install",
|
|
||||||
"i18next": "i18next -c src/renderer/i18n/i18next-parser.config.js",
|
|
||||||
"docker:up": "docker compose --file docker-compose.dev.yml --env-file .env.dev up --detach && docker compose --file docker-compose.dev.yml --env-file .env.dev logs -f",
|
|
||||||
"docker:down": "docker compose --file docker-compose.dev.yml --env-file .env.dev down && docker image rm sonixd_prisma",
|
|
||||||
"docker:migrate": "cd src/server && npx prisma generate && docker exec -ti sonixd_server sh -c \"npx prisma generate && npx prisma db push\"",
|
|
||||||
"docker:reset": "docker exec -ti sonixd_server sh -c \"npx prisma migrate reset && npx prisma db push && npx ts-node prisma/seed.ts\""
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
|
||||||
"*.{js,jsx,ts,tsx}": [
|
|
||||||
"cross-env NODE_ENV=development eslint --cache"
|
|
||||||
],
|
|
||||||
"*.json,.{eslintrc,prettierrc}": [
|
|
||||||
"prettier --ignore-path .eslintignore --parser json --write"
|
|
||||||
],
|
|
||||||
"*.{css,scss}": [
|
|
||||||
"prettier --ignore-path .eslintignore --single-quote --write"
|
|
||||||
],
|
|
||||||
"*.{html,md,yml}": [
|
|
||||||
"prettier --ignore-path .eslintignore --single-quote --write"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"productName": "Sonixd",
|
|
||||||
"appId": "org.erb.sonixd",
|
|
||||||
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
|
|
||||||
"asar": true,
|
|
||||||
"asarUnpack": "**\\*.{node,dll}",
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"package.json"
|
|
||||||
],
|
|
||||||
"afterSign": ".erb/scripts/notarize.js",
|
|
||||||
"mac": {
|
|
||||||
"target": {
|
|
||||||
"target": "default",
|
|
||||||
"arch": [
|
|
||||||
"arm64",
|
|
||||||
"x64"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "distribution",
|
|
||||||
"hardenedRuntime": true,
|
|
||||||
"entitlements": "assets/entitlements.mac.plist",
|
|
||||||
"entitlementsInherit": "assets/entitlements.mac.plist",
|
|
||||||
"gatekeeperAssess": false
|
|
||||||
},
|
|
||||||
"dmg": {
|
|
||||||
"contents": [
|
|
||||||
{
|
|
||||||
"x": 130,
|
|
||||||
"y": 220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"x": 410,
|
|
||||||
"y": 220,
|
|
||||||
"type": "link",
|
|
||||||
"path": "/Applications"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"win": {
|
|
||||||
"target": [
|
|
||||||
"nsis",
|
|
||||||
"zip"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"target": [
|
|
||||||
"AppImage",
|
|
||||||
"tar.xz"
|
|
||||||
],
|
|
||||||
"icon": "assets/icons/placeholder.png",
|
|
||||||
"category": "Development"
|
|
||||||
},
|
|
||||||
"directories": {
|
|
||||||
"app": "release/app",
|
|
||||||
"buildResources": "assets",
|
|
||||||
"output": "release/build"
|
|
||||||
},
|
|
||||||
"extraResources": [
|
|
||||||
"./assets/**"
|
|
||||||
],
|
|
||||||
"publish": {
|
|
||||||
"provider": "github",
|
|
||||||
"owner": "jeffvli",
|
|
||||||
"repo": "sonixd"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/jeffvli/sonixd.git"
|
|
||||||
},
|
|
||||||
"author": {
|
|
||||||
"name": "jeffvli",
|
|
||||||
"url": "https://github.com/jeffvli/"
|
|
||||||
},
|
|
||||||
"contributors": [],
|
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"bugs": {
|
"main": "release/app/dist/main/index.js",
|
||||||
"url": "https://github.com/jeffvli/sonixd/issues"
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build && electron-builder",
|
||||||
|
"postinstall": "node post-install.js && electron-builder install-app-deps",
|
||||||
|
"prisma:init": "npx prisma migrate dev",
|
||||||
|
"prisma:dev": "npx prisma db push",
|
||||||
|
"prisma:migrate": ""
|
||||||
},
|
},
|
||||||
"keywords": [
|
"engines": {
|
||||||
"subsonic",
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
"navidrome",
|
|
||||||
"airsonic",
|
|
||||||
"jellyfin",
|
|
||||||
"react",
|
|
||||||
"electron"
|
|
||||||
],
|
|
||||||
"homepage": "https://github.com/jeffvli/sonixd",
|
|
||||||
"jest": {
|
|
||||||
"testURL": "http://localhost/",
|
|
||||||
"testEnvironment": "jsdom",
|
|
||||||
"transform": {
|
|
||||||
"\\.(ts|tsx|js|jsx)$": "ts-jest"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"jsx",
|
|
||||||
"ts",
|
|
||||||
"tsx",
|
|
||||||
"json"
|
|
||||||
],
|
|
||||||
"moduleDirectories": [
|
|
||||||
"node_modules",
|
|
||||||
"release/app/node_modules"
|
|
||||||
],
|
|
||||||
"testPathIgnorePatterns": [
|
|
||||||
"release/app/dist"
|
|
||||||
],
|
|
||||||
"setupFiles": [
|
|
||||||
"./.erb/scripts/check-build-exists.ts"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
|
|
||||||
"@stylelint/postcss-css-in-js": "^0.38.0",
|
|
||||||
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
|
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
|
||||||
"@testing-library/react": "^13.0.0",
|
|
||||||
"@types/jest": "^27.4.1",
|
|
||||||
"@types/lodash": "^4.14.182",
|
|
||||||
"@types/md5": "^2.3.2",
|
|
||||||
"@types/node": "^17.0.23",
|
|
||||||
"@types/react": "^17.0.43",
|
|
||||||
"@types/react-dom": "^17.0.14",
|
|
||||||
"@types/react-lazy-load-image-component": "^1.5.2",
|
|
||||||
"@types/react-slider": "^1.3.1",
|
|
||||||
"@types/react-test-renderer": "^17.0.1",
|
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
|
||||||
"@types/react-window": "^1.8.5",
|
|
||||||
"@types/react-window-infinite-loader": "^1.0.6",
|
|
||||||
"@types/styled-components": "^5.1.25",
|
|
||||||
"@types/terser-webpack-plugin": "^5.0.4",
|
|
||||||
"@types/webpack-bundle-analyzer": "^4.4.1",
|
|
||||||
"@types/webpack-env": "^1.16.3",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.18.0",
|
|
||||||
"@typescript-eslint/parser": "^5.18.0",
|
|
||||||
"browserslist-config-erb": "^0.0.3",
|
|
||||||
"chalk": "^4.1.2",
|
|
||||||
"concurrently": "^7.1.0",
|
|
||||||
"core-js": "^3.21.1",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"css-loader": "^6.7.1",
|
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
|
||||||
"detect-port": "^1.3.0",
|
|
||||||
"electron": "^18.0.1",
|
|
||||||
"electron-builder": "^23.0.3",
|
|
||||||
"electron-devtools-installer": "^3.2.0",
|
|
||||||
"electron-notarize": "^1.2.1",
|
|
||||||
"electron-rebuild": "^3.2.7",
|
|
||||||
"electronmon": "^2.0.2",
|
|
||||||
"eslint": "^8.12.0",
|
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
|
||||||
"eslint-config-erb": "^4.0.3",
|
|
||||||
"eslint-import-resolver-typescript": "^2.7.1",
|
|
||||||
"eslint-import-resolver-webpack": "^0.13.2",
|
|
||||||
"eslint-plugin-compat": "^4.0.2",
|
|
||||||
"eslint-plugin-import": "^2.26.0",
|
|
||||||
"eslint-plugin-jest": "^26.1.3",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
|
||||||
"eslint-plugin-react": "^7.29.4",
|
|
||||||
"eslint-plugin-react-hooks": "^4.4.0",
|
|
||||||
"eslint-plugin-sort-keys-fix": "^1.1.2",
|
|
||||||
"eslint-plugin-typescript-sort-keys": "^2.1.0",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"html-webpack-plugin": "^5.5.0",
|
|
||||||
"husky": "^7.0.4",
|
|
||||||
"i18next-parser": "^6.3.0",
|
|
||||||
"identity-obj-proxy": "^3.0.0",
|
|
||||||
"jest": "^27.5.1",
|
|
||||||
"lint-staged": "^12.3.7",
|
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
|
||||||
"postcss-scss": "^4.0.4",
|
|
||||||
"postcss-syntax": "^0.36.2",
|
|
||||||
"prettier": "^2.6.2",
|
|
||||||
"react-refresh": "^0.12.0",
|
|
||||||
"react-refresh-typescript": "^2.0.4",
|
|
||||||
"react-test-renderer": "^18.0.0",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"sass": "^1.49.11",
|
|
||||||
"sass-loader": "^12.6.0",
|
|
||||||
"style-loader": "^3.3.1",
|
|
||||||
"stylelint": "^14.9.1",
|
|
||||||
"stylelint-config-rational-order": "^0.1.2",
|
|
||||||
"stylelint-config-standard-scss": "^4.0.0",
|
|
||||||
"stylelint-config-styled-components": "^0.1.1",
|
|
||||||
"stylelint-order": "^5.0.0",
|
|
||||||
"stylelint-processor-styled-components": "^1.10.0",
|
|
||||||
"terser-webpack-plugin": "^5.3.1",
|
|
||||||
"ts-jest": "^27.1.4",
|
|
||||||
"ts-loader": "^9.2.8",
|
|
||||||
"ts-node": "^10.7.0",
|
|
||||||
"typescript": "^4.6.4",
|
|
||||||
"typescript-plugin-styled-components": "^2.0.0",
|
|
||||||
"url-loader": "^4.1.1",
|
|
||||||
"webpack": "^5.71.0",
|
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
|
||||||
"webpack-cli": "^4.9.2",
|
|
||||||
"webpack-dev-server": "^4.8.0",
|
|
||||||
"webpack-merge": "^5.8.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jellyfin/client-axios": "^10.7.8",
|
"@prisma/client": "^4.4.0",
|
||||||
"@mantine/core": "^5.0.0",
|
"node-mpv": "^2.0.0-beta.2"
|
||||||
"@mantine/form": "^5.0.0",
|
},
|
||||||
"@mantine/hooks": "^5.0.0",
|
"devDependencies": {
|
||||||
"axios": "^0.26.1",
|
"@types/react": "^18.0.21",
|
||||||
"electron-debug": "^3.2.0",
|
"@types/react-dom": "^18.0.6",
|
||||||
"electron-log": "^4.4.6",
|
"@vitejs/plugin-react": "^2.1.0",
|
||||||
"electron-updater": "^4.6.5",
|
"electron": "^21.1.0",
|
||||||
"format-duration": "^2.0.0",
|
"electron-builder": "^23.3.3",
|
||||||
"framer-motion": "^6.4.2",
|
"react": "^18.2.0",
|
||||||
"history": "^5.3.0",
|
"react-dom": "^18.2.0",
|
||||||
"i18next": "^21.6.16",
|
"sass": "^1.55.0",
|
||||||
|
"typescript": "^4.8.4",
|
||||||
|
"vite": "^3.1.4",
|
||||||
|
"vite-electron-plugin": "^0.4.4",
|
||||||
|
"@emotion/react": "^11.10.4",
|
||||||
|
"@emotion/styled": "^11.10.4",
|
||||||
|
"@mantine/carousel": "^5.5.4",
|
||||||
|
"@mantine/core": "^5.5.4",
|
||||||
|
"@mantine/dates": "^5.5.4",
|
||||||
|
"@mantine/form": "^5.5.4",
|
||||||
|
"@mantine/hooks": "^5.5.4",
|
||||||
|
"@mantine/modals": "^5.5.4",
|
||||||
|
"@mantine/notifications": "^5.5.4",
|
||||||
|
"@mantine/spotlight": "^5.5.4",
|
||||||
|
"@tanstack/react-query": "^4.10.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.39.0",
|
||||||
|
"@typescript-eslint/parser": "^5.39.0",
|
||||||
|
"ag-grid-community": "^28.2.0",
|
||||||
|
"ag-grid-react": "^28.2.0",
|
||||||
|
"axios": "^1.0.0",
|
||||||
|
"dayjs": "^1.11.5",
|
||||||
|
"electron-is-dev": "^2.0.0",
|
||||||
|
"electron-rebuild": "^3.2.9",
|
||||||
|
"embla-carousel-react": "^7.0.3",
|
||||||
|
"eslint": "^8.24.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-import-resolver-typescript": "^3.5.1",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"eslint-plugin-promise": "^6.0.1",
|
||||||
|
"eslint-plugin-react": "^7.31.8",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-sort-keys-fix": "^1.1.2",
|
||||||
|
"eslint-plugin-typescript-sort-keys": "^2.1.0",
|
||||||
"immer": "^9.0.15",
|
"immer": "^9.0.15",
|
||||||
"is-electron": "^2.2.1",
|
"nanoid": "^4.0.0",
|
||||||
"lodash": "^4.17.21",
|
"prettier": "^2.7.1",
|
||||||
"md5": "^2.3.0",
|
"prisma": "^4.4.0",
|
||||||
"nanoid": "^3.3.3",
|
"react-virtualized-auto-sizer": "^1.0.7",
|
||||||
"net": "^1.0.2",
|
|
||||||
"node-mpv": "^2.0.0-beta.2",
|
|
||||||
"react": "^18.0.0",
|
|
||||||
"react-dom": "^18.0.0",
|
|
||||||
"react-helmet-async": "^1.3.0",
|
|
||||||
"react-i18next": "^11.16.7",
|
|
||||||
"react-lazy-load-image-component": "^1.5.4",
|
|
||||||
"react-player": "^2.10.0",
|
|
||||||
"react-query": "^4.0.0-beta.23",
|
|
||||||
"react-router": "^6.3.0",
|
|
||||||
"react-router-dom": "^6.3.0",
|
|
||||||
"react-slider": "^2.0.0",
|
|
||||||
"react-spaces": "^0.3.4",
|
|
||||||
"react-use": "^17.3.2",
|
|
||||||
"react-virtualized-auto-sizer": "^1.0.6",
|
|
||||||
"react-window": "^1.8.7",
|
"react-window": "^1.8.7",
|
||||||
"react-window-infinite-loader": "^1.0.8",
|
"react-window-infinite-loader": "^1.0.8",
|
||||||
"styled-components": "^5.3.5",
|
"replace-in-file": "^6.3.5",
|
||||||
"tabler-icons-react": "^1.46.0",
|
"ts-node": "^10.9.1",
|
||||||
"zustand": "^4.0.0-rc.1"
|
"vite-plugin-electron": "^0.9.2",
|
||||||
|
"vite-plugin-electron-renderer": "^0.9.3",
|
||||||
|
"zustand": "^4.1.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"debug": {
|
||||||
"styled-components": "^5"
|
"env": {
|
||||||
},
|
"VITE_DEV_SERVER_URL": "http://127.0.0.1:7777"
|
||||||
"devEngines": {
|
|
||||||
"node": ">=14.x",
|
|
||||||
"npm": ">=7.x"
|
|
||||||
},
|
|
||||||
"browserslist": [],
|
|
||||||
"prettier": {
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
".prettierrc",
|
|
||||||
".eslintrc"
|
|
||||||
],
|
|
||||||
"options": {
|
|
||||||
"parser": "json"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const replace = require('replace-in-file');
|
||||||
|
|
||||||
|
// fix long prisma loading times caused by scanning from process.cwd(), which returns "/" when run in electron
|
||||||
|
// (thus it scans all files on the computer.) See https://github.com/prisma/prisma/issues/8484
|
||||||
|
// solution: we get the app path from main process via sync IPC
|
||||||
|
const options = {
|
||||||
|
files: path.join(__dirname, '../release/app/node_modules/', '@prisma', 'client', 'index.js'),
|
||||||
|
from: 'findSync(process.cwd()',
|
||||||
|
to: `findSync(require("electron").ipcRenderer.sendSync('config:get-app-path')`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const results = replace.sync(options);
|
||||||
|
console.log('build script: prisma fix', results);
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
engineType = "library"
|
||||||
|
binaryTargets = ["native", "windows"]
|
||||||
|
// output = "../release/app/node_modules/.prisma/client"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = "file:../release/app/prisma/dev.db"
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
favorites Favorite[]
|
||||||
|
albumArtistRatings AlbumArtistRating[]
|
||||||
|
artistRatings ArtistRating[]
|
||||||
|
albumRatings AlbumRating[]
|
||||||
|
songRatings SongRating[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Server {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
nickname String @unique
|
||||||
|
url String @unique
|
||||||
|
remoteId String @map("remote_id")
|
||||||
|
authUsername String @map("auth_username")
|
||||||
|
authCredential String @map("auth_credential")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
serverType ServerType @relation(fields: [serverTypeId], references: [id])
|
||||||
|
serverTypeId Int
|
||||||
|
|
||||||
|
serverFolders ServerFolder[]
|
||||||
|
songs Song[]
|
||||||
|
albums Album[]
|
||||||
|
artists Artist[]
|
||||||
|
albumArtists AlbumArtist[]
|
||||||
|
|
||||||
|
// @@map("server")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ServerType {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @unique
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
Server Server[]
|
||||||
|
|
||||||
|
// @@map("server_type")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ServerFolder {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
remoteId String @map("remote_id")
|
||||||
|
enabled Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
server Server @relation(fields: [serverId], references: [id])
|
||||||
|
serverId Int
|
||||||
|
|
||||||
|
// @@map("server_folder")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Genre {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @unique
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
artists Artist[]
|
||||||
|
albumArtists AlbumArtist[]
|
||||||
|
albums Album[]
|
||||||
|
songs Song[]
|
||||||
|
|
||||||
|
// @@map("genre")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Favorite {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
albumArtists AlbumArtist[]
|
||||||
|
artists Artist[]
|
||||||
|
albums Album[]
|
||||||
|
songs Song[]
|
||||||
|
|
||||||
|
User User? @relation(fields: [userId], references: [id])
|
||||||
|
userId Int?
|
||||||
|
|
||||||
|
// @@map("favorite")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AlbumArtistRating {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
value Float
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId Int
|
||||||
|
|
||||||
|
albumArtist AlbumArtist? @relation(fields: [albumArtistId], references: [id])
|
||||||
|
albumArtistId Int
|
||||||
|
// @@map("album_artist_rating")
|
||||||
|
|
||||||
|
@@unique(fields: [albumArtistId, userId], name: "uniqueAlbumArtistRating", map: "unique_album_artist_rating")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ArtistRating {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
value Float
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId Int
|
||||||
|
|
||||||
|
artist Artist? @relation(fields: [artistId], references: [id])
|
||||||
|
artistId Int
|
||||||
|
// @@map("artist_rating")
|
||||||
|
|
||||||
|
@@unique(fields: [artistId, userId], name: "uniqueArtistRating", map: "unique_artist_rating")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AlbumRating {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
value Float
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId Int
|
||||||
|
|
||||||
|
album Album? @relation(fields: [albumId], references: [id])
|
||||||
|
albumId Int
|
||||||
|
// @@map("album_rating")
|
||||||
|
|
||||||
|
@@unique(fields: [albumId, userId], name: "uniqueAlbumRating", map: "unique_album_rating")
|
||||||
|
}
|
||||||
|
|
||||||
|
model SongRating {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
value Float
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId Int
|
||||||
|
|
||||||
|
song Song? @relation(fields: [songId], references: [id])
|
||||||
|
songId Int
|
||||||
|
// @@map("song_rating")
|
||||||
|
|
||||||
|
@@unique(fields: [songId, userId], name: "uniqueSongRating", map: "unique_song_rating")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AlbumArtist {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
image String?
|
||||||
|
image_remote String? @map("image_remote")
|
||||||
|
sortName String @map("sort_name")
|
||||||
|
biography String?
|
||||||
|
remoteId String @map("remote_id")
|
||||||
|
remoteCreatedAt DateTime? @map("remote_created_at")
|
||||||
|
deleted Boolean @default(false)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
genres Genre[]
|
||||||
|
albums Album[]
|
||||||
|
songs Song[]
|
||||||
|
favorites Favorite[]
|
||||||
|
ratings AlbumArtistRating[]
|
||||||
|
|
||||||
|
server Server @relation(fields: [serverId], references: [id])
|
||||||
|
serverId Int
|
||||||
|
// @@map("album_artist")
|
||||||
|
|
||||||
|
@@unique(fields: [serverId, remoteId], name: "uniqueAlbumArtistId", map: "unique_album_artist_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Artist {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
image String?
|
||||||
|
image_remote String? @map("image_remote")
|
||||||
|
biography String?
|
||||||
|
remoteId String @map("remote_id")
|
||||||
|
remoteCreatedAt DateTime? @map("remote_created_at")
|
||||||
|
deleted Boolean @default(false)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
genres Genre[]
|
||||||
|
favorites Favorite[]
|
||||||
|
ratings ArtistRating[]
|
||||||
|
|
||||||
|
server Server @relation(fields: [serverId], references: [id])
|
||||||
|
serverId Int
|
||||||
|
// @@map("artist")
|
||||||
|
|
||||||
|
@@unique(fields: [serverId, remoteId], name: "uniqueArtistId", map: "unique_artist_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Album {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
image String?
|
||||||
|
image_remote String? @map("image_remote")
|
||||||
|
releaseDate DateTime? @map("release_date")
|
||||||
|
releaseYear Int? @map("release_year")
|
||||||
|
remoteId String
|
||||||
|
remoteCreatedAt DateTime?
|
||||||
|
deleted Boolean @default(false)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
genres Genre[]
|
||||||
|
albumArtists AlbumArtist[]
|
||||||
|
favorites Favorite[]
|
||||||
|
ratings AlbumRating[]
|
||||||
|
|
||||||
|
server Server @relation(fields: [serverId], references: [id])
|
||||||
|
serverId Int @map("server_id")
|
||||||
|
// @@map("album")
|
||||||
|
|
||||||
|
@@unique(fields: [serverId, remoteId], name: "uniqueAlbumId", map: "unique_album_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Song {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
image String?
|
||||||
|
remote_image String? @map("remote_image")
|
||||||
|
releaseDate DateTime? @map("release_date")
|
||||||
|
releaseYear Int? @map("release_year")
|
||||||
|
duration Float?
|
||||||
|
lyric String?
|
||||||
|
bitRate Int @map("bit_rate")
|
||||||
|
container String
|
||||||
|
size String?
|
||||||
|
channels Int?
|
||||||
|
discIndex Int @default(1) @map("disc_index")
|
||||||
|
trackIndex Int? @map("track_index")
|
||||||
|
artistName String? @map("artist_name")
|
||||||
|
remoteId String @map("remote_id")
|
||||||
|
remoteCreatedAt DateTime? @map("remote_created_at")
|
||||||
|
deleted Boolean @default(false)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
genres Genre[]
|
||||||
|
albumArtists AlbumArtist[]
|
||||||
|
favorites Favorite[]
|
||||||
|
ratings SongRating[]
|
||||||
|
|
||||||
|
server Server @relation(fields: [serverId], references: [id])
|
||||||
|
serverId Int @map("server_id")
|
||||||
|
// @@map("song")
|
||||||
|
|
||||||
|
@@unique(fields: [serverId, remoteId], name: "uniqueSongId", map: "unique_song_id")
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 9.6 MiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 62 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#41D1FF"/>
|
||||||
|
<stop offset="1" stop-color="#BD34FE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFEA83"/>
|
||||||
|
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||||
|
<stop offset="1" stop-color="#FFA800"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
|
||||||
|
<g fill="#61DAFB">
|
||||||
|
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
|
||||||
|
<circle cx="420.9" cy="296.5" r="45.7"/>
|
||||||
|
<path d="M520.5 78.1z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#41D1FF"/>
|
||||||
|
<stop offset="1" stop-color="#BD34FE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFEA83"/>
|
||||||
|
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||||
|
<stop offset="1" stop-color="#FFA800"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,14 +1,98 @@
|
|||||||
{
|
{
|
||||||
"name": "sonixd",
|
"name": "sonixd-rewrite",
|
||||||
"version": "1.0.0-alpha1",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sonixd",
|
"name": "sonixd-rewrite",
|
||||||
"version": "1.0.0-alpha1",
|
"version": "0.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "4.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prisma": "4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/client": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-ciKOP246x1xwr04G9ajHlJ4pkmtu9Q6esVyqVBO0QJihaKQIUvbPjClp17IsRJyxqNpFm4ScbOc/s9DUzKHINQ==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT"
|
"dependencies": {
|
||||||
|
"@prisma/engines-version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prisma": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"prisma": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines-version": {
|
||||||
|
"version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6.tgz",
|
||||||
|
"integrity": "sha512-P5v/PuEIJLYXZUZBvOLPqoyCW+m6StNqHdiR6te++gYVODpPdLakks5HVx3JaZIY+LwR02juJWFlwpc9Eog/ug=="
|
||||||
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/engines": "4.4.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js",
|
||||||
|
"prisma2": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-ciKOP246x1xwr04G9ajHlJ4pkmtu9Q6esVyqVBO0QJihaKQIUvbPjClp17IsRJyxqNpFm4ScbOc/s9DUzKHINQ==",
|
||||||
|
"requires": {
|
||||||
|
"@prisma/engines-version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@prisma/engines": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"@prisma/engines-version": {
|
||||||
|
"version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6.tgz",
|
||||||
|
"integrity": "sha512-P5v/PuEIJLYXZUZBvOLPqoyCW+m6StNqHdiR6te++gYVODpPdLakks5HVx3JaZIY+LwR02juJWFlwpc9Eog/ug=="
|
||||||
|
},
|
||||||
|
"prisma": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"requires": {
|
||||||
|
"@prisma/engines": "4.4.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "sonixd",
|
"name": "sonixd-rewrite",
|
||||||
"version": "1.0.0-alpha1",
|
"version": "0.0.0",
|
||||||
"description": "A full-featured Subsonic/Jellyfin compatible desktop client",
|
"description": "A foundation for scalable desktop apps",
|
||||||
"main": "./dist/main/main.js",
|
"main": "./dist/main/main.js",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "jeffvli",
|
"name": "Electron React Boilerplate Maintainers",
|
||||||
"url": "https://github.com/jeffvli/"
|
"email": "electronreactboilerplate@gmail.com",
|
||||||
|
"url": "https://github.com/electron-react-boilerplate"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {},
|
||||||
"electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js",
|
"dependencies": {
|
||||||
"link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts",
|
"@prisma/client": "4.4.0"
|
||||||
"postinstall": "npm run electron-rebuild && npm run link-modules"
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prisma": "4.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import electron from "/electron.png";
|
||||||
|
import react from "/react.svg";
|
||||||
|
import vite from "/vite.svg";
|
||||||
|
import styles from "styles/app.module.scss";
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
|
||||||
|
window.electron.doThing();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.app}>
|
||||||
|
<header className={styles.appHeader}>
|
||||||
|
<div className={styles.logos}>
|
||||||
|
<div className={styles.imgBox}>
|
||||||
|
<img
|
||||||
|
src={electron}
|
||||||
|
style={{ height: "24vw" }}
|
||||||
|
className={styles.appLogo}
|
||||||
|
alt="electron"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.imgBox}>
|
||||||
|
<img src={vite} style={{ height: "19vw" }} alt="vite" />
|
||||||
|
</div>
|
||||||
|
<div className={styles.imgBox}>
|
||||||
|
<img
|
||||||
|
src={react}
|
||||||
|
style={{ maxWidth: "100%" }}
|
||||||
|
className={styles.appLogo}
|
||||||
|
alt="logo"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Hello Electron + Vite + React!</p>
|
||||||
|
<p>
|
||||||
|
<button onClick={() => setCount((count) => count + 1)}>
|
||||||
|
count is: {count}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Edit <code>App.tsx</code> and save to test HMR updates.
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
className={styles.appLink}
|
||||||
|
href="https://reactjs.org"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Learn React
|
||||||
|
</a>
|
||||||
|
{" | "}
|
||||||
|
<a
|
||||||
|
className={styles.appLink}
|
||||||
|
href="https://vitejs.dev/guide/features.html"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Vite Docs
|
||||||
|
</a>
|
||||||
|
<div className={styles.staticPublic}>
|
||||||
|
Place static files into the <code>/public</code> folder
|
||||||
|
<img style={{ width: 77 }} src="./node.png" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import '@testing-library/jest-dom';
|
|
||||||
// import { render } from '@testing-library/react';
|
|
||||||
// import { App } from 'renderer/app';
|
|
||||||
|
|
||||||
describe('App', () => {
|
|
||||||
// eslint-disable-next-line jest/no-commented-out-tests
|
|
||||||
// it('should render', () => {
|
|
||||||
// expect(render(<App />)).toBeTruthy();
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
.app {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.appHeader {
|
||||||
|
background-color: #282c34;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: calc(10px + 2vmin);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
.logos {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 5vw;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.imgBox {
|
||||||
|
width: 33.33%;
|
||||||
|
|
||||||
|
.appLogo {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.appLogo {
|
||||||
|
animation: App-logo-spin infinite 20s linear;
|
||||||
|
}
|
||||||
|
@keyframes App-logo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: calc(10px + 2vmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.appLink {
|
||||||
|
color: #61dafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.staticPublic {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 4px 7px;
|
||||||
|
margin: 0 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgb(30, 30, 30, .7);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
|
type ApiSurface = {
|
||||||
|
prisma: {
|
||||||
|
server: {
|
||||||
|
getServer: () => Promise<Prisma.ServerSelect>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electron: ApiSurface;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import i18n from 'i18next';
|
|
||||||
import { initReactI18next } from 'react-i18next';
|
|
||||||
const en = require('./locales/en.json');
|
|
||||||
|
|
||||||
const resources = {
|
|
||||||
en: { translation: en },
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Languages = [
|
|
||||||
{
|
|
||||||
label: 'English',
|
|
||||||
value: 'en',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
i18n
|
|
||||||
.use(initReactI18next) // passes i18n down to react-i18next
|
|
||||||
.init({
|
|
||||||
fallbackLng: 'en',
|
|
||||||
// language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
|
|
||||||
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
|
|
||||||
// if you're using a language detector, do not define the lng option
|
|
||||||
interpolation: {
|
|
||||||
escapeValue: false, // react already safes from xss
|
|
||||||
},
|
|
||||||
|
|
||||||
lng: 'en',
|
|
||||||
|
|
||||||
resources,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default i18n;
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
// i18next-parser.config.js
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
contextSeparator: '_',
|
|
||||||
// Key separator used in your translation keys
|
|
||||||
|
|
||||||
createOldCatalogs: true,
|
|
||||||
|
|
||||||
// Exit with an exit code of 1 when translations are updated (for CI purpose)
|
|
||||||
customValueTemplate: null,
|
|
||||||
|
|
||||||
// Save the \_old files
|
|
||||||
defaultNamespace: 'translation',
|
|
||||||
|
|
||||||
// Default namespace used in your i18next config
|
|
||||||
defaultValue: '',
|
|
||||||
|
|
||||||
// Exit with an exit code of 1 on warnings
|
|
||||||
failOnUpdate: false,
|
|
||||||
|
|
||||||
// Display info about the parsing including some stats
|
|
||||||
failOnWarnings: false,
|
|
||||||
|
|
||||||
// The locale to compare with default values to determine whether a default value has been changed.
|
|
||||||
// If this is set and a default value differs from a translation in the specified locale, all entries
|
|
||||||
// for that key across locales are reset to the default value, and existing translations are moved to
|
|
||||||
// the `_old` file.
|
|
||||||
i18nextOptions: null,
|
|
||||||
|
|
||||||
// Default value to give to empty keys
|
|
||||||
// You may also specify a function accepting the locale, namespace, and key as arguments
|
|
||||||
indentation: 2,
|
|
||||||
|
|
||||||
// Plural separator used in your translation keys
|
|
||||||
// If you want to use plain english keys, separators such as `_` might conflict. You might want to set `pluralSeparator` to a different string that does not occur in your keys.
|
|
||||||
input: [
|
|
||||||
'../components/**/*.{js,jsx,ts,tsx}',
|
|
||||||
'../features/**/*.{js,jsx,ts,tsx}',
|
|
||||||
'../layouts/**/*.{js,jsx,ts,tsx}',
|
|
||||||
'!../../src/node_modules/**',
|
|
||||||
'!../../src/**/*.prod.js',
|
|
||||||
],
|
|
||||||
|
|
||||||
// Indentation of the catalog files
|
|
||||||
keepRemoved: false,
|
|
||||||
|
|
||||||
// Keep keys from the catalog that are no longer in code
|
|
||||||
keySeparator: '.',
|
|
||||||
|
|
||||||
// Key separator used in your translation keys
|
|
||||||
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
|
|
||||||
// see below for more details
|
|
||||||
lexers: {
|
|
||||||
default: ['JavascriptLexer'],
|
|
||||||
handlebars: ['HandlebarsLexer'],
|
|
||||||
|
|
||||||
hbs: ['HandlebarsLexer'],
|
|
||||||
htm: ['HTMLLexer'],
|
|
||||||
|
|
||||||
html: ['HTMLLexer'],
|
|
||||||
js: ['JavascriptLexer'],
|
|
||||||
jsx: ['JsxLexer'],
|
|
||||||
|
|
||||||
mjs: ['JavascriptLexer'],
|
|
||||||
// if you're writing jsx inside .js files, change this to JsxLexer
|
|
||||||
ts: ['JavascriptLexer'],
|
|
||||||
|
|
||||||
tsx: ['JsxLexer'],
|
|
||||||
},
|
|
||||||
|
|
||||||
lineEnding: 'auto',
|
|
||||||
|
|
||||||
// Control the line ending. See options at https://github.com/ryanve/eol
|
|
||||||
locales: ['en'],
|
|
||||||
|
|
||||||
// An array of the locales in your applications
|
|
||||||
namespaceSeparator: false,
|
|
||||||
|
|
||||||
// Namespace separator used in your translation keys
|
|
||||||
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
|
|
||||||
output: 'src/renderer/i18n/locales/$LOCALE.json',
|
|
||||||
|
|
||||||
// Supports $LOCALE and $NAMESPACE injection
|
|
||||||
// Supports JSON (.json) and YAML (.yml) file formats
|
|
||||||
// Where to write the locale files relative to process.cwd()
|
|
||||||
pluralSeparator: '_',
|
|
||||||
|
|
||||||
// If you wish to customize the value output the value as an object, you can set your own format.
|
|
||||||
// ${defaultValue} is the default value you set in your translation function.
|
|
||||||
// Any other custom property will be automatically extracted.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// {
|
|
||||||
// message: "${defaultValue}",
|
|
||||||
// description: "${maxLength}", //
|
|
||||||
// }
|
|
||||||
resetDefaultValueLocale: 'en',
|
|
||||||
|
|
||||||
// Whether or not to sort the catalog. Can also be a [compareFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#parameters)
|
|
||||||
skipDefaultValues: false,
|
|
||||||
|
|
||||||
// An array of globs that describe where to look for source files
|
|
||||||
// relative to the location of the configuration file
|
|
||||||
sort: true,
|
|
||||||
|
|
||||||
// Whether to ignore default values
|
|
||||||
// You may also specify a function accepting the locale and namespace as arguments
|
|
||||||
useKeysAsDefaultValue: true,
|
|
||||||
|
|
||||||
// Whether to use the keys as the default value; ex. "Hello": "Hello", "World": "World"
|
|
||||||
// This option takes precedence over the `defaultValue` and `skipDefaultValues` options
|
|
||||||
// You may also specify a function accepting the locale and namespace as arguments
|
|
||||||
verbose: false,
|
|
||||||
// If you wish to customize options in internally used i18next instance, you can define an object with any
|
|
||||||
// configuration property supported by i18next (https://www.i18next.com/overview/configuration-options).
|
|
||||||
// { compatibilityJSON: 'v3' } can be used to generate v3 compatible plurals.
|
|
||||||
};
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"player": {
|
|
||||||
"next": "player.next",
|
|
||||||
"play": "player.play",
|
|
||||||
"prev": "player.prev",
|
|
||||||
"seekBack": "player.seekBack",
|
|
||||||
"seekForward": "player.seekForward"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import App from './App';
|
||||||
|
import 'styles/index.css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you enables use of Node.js API in the Renderer-process
|
||||||
|
* ```
|
||||||
|
* npm i -D vite-plugin-electron-renderer
|
||||||
|
* ```
|
||||||
|
* @see - https://github.com/electron-vite/vite-plugin-electron/tree/main/packages/electron-renderer#electron-renderervite-serve
|
||||||
|
*/
|
||||||
|
// import './samples/node-api'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
postMessage({ payload: 'removeLoading' }, '*');
|
||||||
@@ -1 +0,0 @@
|
|||||||
import './player';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
declare module 'node-mpv';
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import './core';
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-dynamic-require
|
|
||||||
require(`./${process.platform}`);
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
/* eslint global-require: off, no-console: off, promise/always-return: off */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This module executes inside of electron's main process. You can start
|
|
||||||
* electron renderer process from here and communicate with the other processes
|
|
||||||
* through IPC.
|
|
||||||
*
|
|
||||||
* When running `npm run build` or `npm run build:main`, this file is compiled to
|
|
||||||
* `./src/main.js` using webpack. This gives us some performance wins.
|
|
||||||
*/
|
|
||||||
import path from 'path';
|
|
||||||
import { app, BrowserWindow, shell, ipcMain } from 'electron';
|
|
||||||
import log from 'electron-log';
|
|
||||||
import { autoUpdater } from 'electron-updater';
|
|
||||||
import MenuBuilder from './menu';
|
|
||||||
import { resolveHtmlPath } from './utils';
|
|
||||||
import './features';
|
|
||||||
|
|
||||||
export default class AppUpdater {
|
|
||||||
constructor() {
|
|
||||||
log.transports.file.level = 'info';
|
|
||||||
autoUpdater.logger = log;
|
|
||||||
autoUpdater.checkForUpdatesAndNotify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
const sourceMapSupport = require('source-map-support');
|
|
||||||
sourceMapSupport.install();
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDevelopment =
|
|
||||||
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true';
|
|
||||||
|
|
||||||
if (isDevelopment) {
|
|
||||||
require('electron-debug')();
|
|
||||||
}
|
|
||||||
|
|
||||||
const installExtensions = async () => {
|
|
||||||
const installer = require('electron-devtools-installer');
|
|
||||||
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
|
|
||||||
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
|
|
||||||
|
|
||||||
return installer
|
|
||||||
.default(
|
|
||||||
extensions.map((name) => installer[name]),
|
|
||||||
forceDownload
|
|
||||||
)
|
|
||||||
.catch(console.log);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createWindow = async () => {
|
|
||||||
if (isDevelopment) {
|
|
||||||
await installExtensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
const RESOURCES_PATH = app.isPackaged
|
|
||||||
? path.join(process.resourcesPath, 'assets')
|
|
||||||
: path.join(__dirname, '../../assets');
|
|
||||||
|
|
||||||
const getAssetPath = (...paths: string[]): string => {
|
|
||||||
return path.join(RESOURCES_PATH, ...paths);
|
|
||||||
};
|
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
|
||||||
frame: false,
|
|
||||||
height: 728,
|
|
||||||
icon: getAssetPath('icon.png'),
|
|
||||||
minHeight: 600,
|
|
||||||
minWidth: 640,
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
backgroundThrottling: false,
|
|
||||||
|
|
||||||
contextIsolation: true,
|
|
||||||
devTools: true,
|
|
||||||
nodeIntegration: true,
|
|
||||||
preload: app.isPackaged
|
|
||||||
? path.join(__dirname, 'preload.js')
|
|
||||||
: path.join(__dirname, '../../.erb/dll/preload.js'),
|
|
||||||
},
|
|
||||||
width: 1024,
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('window-maximize', () => {
|
|
||||||
mainWindow?.maximize();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('window-unmaximize', () => {
|
|
||||||
mainWindow?.unmaximize();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('window-minimize', () => {
|
|
||||||
mainWindow?.minimize();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('window-close', () => {
|
|
||||||
mainWindow?.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.loadURL(resolveHtmlPath('index.html'));
|
|
||||||
|
|
||||||
mainWindow.on('ready-to-show', () => {
|
|
||||||
if (!mainWindow) {
|
|
||||||
throw new Error('"mainWindow" is not defined');
|
|
||||||
}
|
|
||||||
if (process.env.START_MINIMIZED) {
|
|
||||||
mainWindow.minimize();
|
|
||||||
} else {
|
|
||||||
mainWindow.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.on('closed', () => {
|
|
||||||
mainWindow = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const menuBuilder = new MenuBuilder(mainWindow);
|
|
||||||
menuBuilder.buildMenu();
|
|
||||||
|
|
||||||
// Open urls in the user's browser
|
|
||||||
mainWindow.webContents.setWindowOpenHandler((edata) => {
|
|
||||||
shell.openExternal(edata.url);
|
|
||||||
return { action: 'deny' };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove this if your app does not use auto updates
|
|
||||||
// eslint-disable-next-line
|
|
||||||
new AppUpdater();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add event listeners...
|
|
||||||
*/
|
|
||||||
|
|
||||||
app.commandLine.appendSwitch(
|
|
||||||
'disable-features',
|
|
||||||
'HardwareMediaKeyHandling,MediaSessionService'
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getMainWindow = () => {
|
|
||||||
return mainWindow;
|
|
||||||
};
|
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
// Respect the OSX convention of having the application in memory even
|
|
||||||
// after all windows have been closed
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app
|
|
||||||
.whenReady()
|
|
||||||
.then(() => {
|
|
||||||
createWindow();
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(console.log);
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
import {
|
|
||||||
app,
|
|
||||||
Menu,
|
|
||||||
shell,
|
|
||||||
BrowserWindow,
|
|
||||||
MenuItemConstructorOptions,
|
|
||||||
} from 'electron';
|
|
||||||
|
|
||||||
interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
|
|
||||||
selector?: string;
|
|
||||||
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MenuBuilder {
|
|
||||||
mainWindow: BrowserWindow;
|
|
||||||
|
|
||||||
constructor(mainWindow: BrowserWindow) {
|
|
||||||
this.mainWindow = mainWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildMenu(): Menu {
|
|
||||||
if (
|
|
||||||
process.env.NODE_ENV === 'development' ||
|
|
||||||
process.env.DEBUG_PROD === 'true'
|
|
||||||
) {
|
|
||||||
this.setupDevelopmentEnvironment();
|
|
||||||
}
|
|
||||||
|
|
||||||
const template =
|
|
||||||
process.platform === 'darwin'
|
|
||||||
? this.buildDarwinTemplate()
|
|
||||||
: this.buildDefaultTemplate();
|
|
||||||
|
|
||||||
const menu = Menu.buildFromTemplate(template);
|
|
||||||
Menu.setApplicationMenu(menu);
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupDevelopmentEnvironment(): void {
|
|
||||||
this.mainWindow.webContents.on('context-menu', (_, props) => {
|
|
||||||
const { x, y } = props;
|
|
||||||
|
|
||||||
Menu.buildFromTemplate([
|
|
||||||
{
|
|
||||||
label: 'Inspect element',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.webContents.inspectElement(x, y);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]).popup({ window: this.mainWindow });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
buildDarwinTemplate(): MenuItemConstructorOptions[] {
|
|
||||||
const subMenuAbout: DarwinMenuItemConstructorOptions = {
|
|
||||||
label: 'Electron',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'About ElectronReact',
|
|
||||||
selector: 'orderFrontStandardAboutPanel:',
|
|
||||||
},
|
|
||||||
{ type: 'separator' },
|
|
||||||
{ label: 'Services', submenu: [] },
|
|
||||||
{ type: 'separator' },
|
|
||||||
{
|
|
||||||
label: 'Hide ElectronReact',
|
|
||||||
accelerator: 'Command+H',
|
|
||||||
selector: 'hide:',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Hide Others',
|
|
||||||
accelerator: 'Command+Shift+H',
|
|
||||||
selector: 'hideOtherApplications:',
|
|
||||||
},
|
|
||||||
{ label: 'Show All', selector: 'unhideAllApplications:' },
|
|
||||||
{ type: 'separator' },
|
|
||||||
{
|
|
||||||
label: 'Quit',
|
|
||||||
accelerator: 'Command+Q',
|
|
||||||
click: () => {
|
|
||||||
app.quit();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const subMenuEdit: DarwinMenuItemConstructorOptions = {
|
|
||||||
label: 'Edit',
|
|
||||||
submenu: [
|
|
||||||
{ label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
|
|
||||||
{ label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
|
|
||||||
{ type: 'separator' },
|
|
||||||
{ label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
|
|
||||||
{ label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
|
|
||||||
{ label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
|
|
||||||
{
|
|
||||||
label: 'Select All',
|
|
||||||
accelerator: 'Command+A',
|
|
||||||
selector: 'selectAll:',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const subMenuViewDev: MenuItemConstructorOptions = {
|
|
||||||
label: 'View',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'Reload',
|
|
||||||
accelerator: 'Command+R',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.webContents.reload();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Toggle Full Screen',
|
|
||||||
accelerator: 'Ctrl+Command+F',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Toggle Developer Tools',
|
|
||||||
accelerator: 'Alt+Command+I',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.webContents.toggleDevTools();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const subMenuViewProd: MenuItemConstructorOptions = {
|
|
||||||
label: 'View',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'Toggle Full Screen',
|
|
||||||
accelerator: 'Ctrl+Command+F',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const subMenuWindow: DarwinMenuItemConstructorOptions = {
|
|
||||||
label: 'Window',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'Minimize',
|
|
||||||
accelerator: 'Command+M',
|
|
||||||
selector: 'performMiniaturize:',
|
|
||||||
},
|
|
||||||
{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:' },
|
|
||||||
{ type: 'separator' },
|
|
||||||
{ label: 'Bring All to Front', selector: 'arrangeInFront:' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const subMenuHelp: MenuItemConstructorOptions = {
|
|
||||||
label: 'Help',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'Learn More',
|
|
||||||
click() {
|
|
||||||
shell.openExternal('https://electronjs.org');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Documentation',
|
|
||||||
click() {
|
|
||||||
shell.openExternal(
|
|
||||||
'https://github.com/electron/electron/tree/main/docs#readme'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Community Discussions',
|
|
||||||
click() {
|
|
||||||
shell.openExternal('https://www.electronjs.org/community');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search Issues',
|
|
||||||
click() {
|
|
||||||
shell.openExternal('https://github.com/electron/electron/issues');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const subMenuView =
|
|
||||||
process.env.NODE_ENV === 'development' ||
|
|
||||||
process.env.DEBUG_PROD === 'true'
|
|
||||||
? subMenuViewDev
|
|
||||||
: subMenuViewProd;
|
|
||||||
|
|
||||||
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
|
|
||||||
}
|
|
||||||
|
|
||||||
buildDefaultTemplate() {
|
|
||||||
const templateDefault = [
|
|
||||||
{
|
|
||||||
label: '&File',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: '&Open',
|
|
||||||
accelerator: 'Ctrl+O',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '&Close',
|
|
||||||
accelerator: 'Ctrl+W',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.close();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '&View',
|
|
||||||
submenu:
|
|
||||||
process.env.NODE_ENV === 'development' ||
|
|
||||||
process.env.DEBUG_PROD === 'true'
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
label: '&Reload',
|
|
||||||
accelerator: 'Ctrl+R',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.webContents.reload();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Toggle &Full Screen',
|
|
||||||
accelerator: 'F11',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.setFullScreen(
|
|
||||||
!this.mainWindow.isFullScreen()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Toggle &Developer Tools',
|
|
||||||
accelerator: 'Alt+Ctrl+I',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.webContents.toggleDevTools();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
label: 'Toggle &Full Screen',
|
|
||||||
accelerator: 'F11',
|
|
||||||
click: () => {
|
|
||||||
this.mainWindow.setFullScreen(
|
|
||||||
!this.mainWindow.isFullScreen()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Help',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'Learn More',
|
|
||||||
click() {
|
|
||||||
shell.openExternal('https://electronjs.org');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Documentation',
|
|
||||||
click() {
|
|
||||||
shell.openExternal(
|
|
||||||
'https://github.com/electron/electron/tree/main/docs#readme'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Community Discussions',
|
|
||||||
click() {
|
|
||||||
shell.openExternal('https://www.electronjs.org/community');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Search Issues',
|
|
||||||
click() {
|
|
||||||
shell.openExternal('https://github.com/electron/electron/issues');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return templateDefault;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
|
|
||||||
import { PlayerData } from 'renderer/store';
|
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('electron', {
|
|
||||||
ipcRenderer: {
|
|
||||||
PLAYER_CURRENT_TIME() {
|
|
||||||
ipcRenderer.send('player-current-time');
|
|
||||||
},
|
|
||||||
PLAYER_MUTE() {
|
|
||||||
ipcRenderer.send('player-mute');
|
|
||||||
},
|
|
||||||
PLAYER_NEXT() {
|
|
||||||
ipcRenderer.send('player-next');
|
|
||||||
},
|
|
||||||
PLAYER_PAUSE() {
|
|
||||||
ipcRenderer.send('player-pause');
|
|
||||||
},
|
|
||||||
PLAYER_PLAY() {
|
|
||||||
ipcRenderer.send('player-play');
|
|
||||||
},
|
|
||||||
PLAYER_PREVIOUS() {
|
|
||||||
ipcRenderer.send('player-previous');
|
|
||||||
},
|
|
||||||
PLAYER_SEEK(seconds: number) {
|
|
||||||
ipcRenderer.send('player-seek', seconds);
|
|
||||||
},
|
|
||||||
PLAYER_SEEK_TO(seconds: number) {
|
|
||||||
ipcRenderer.send('player-seek-to', seconds);
|
|
||||||
},
|
|
||||||
PLAYER_SET_QUEUE(data: PlayerData) {
|
|
||||||
ipcRenderer.send('player-set-queue', data);
|
|
||||||
},
|
|
||||||
PLAYER_SET_QUEUE_NEXT(data: PlayerData) {
|
|
||||||
ipcRenderer.send('player-set-queue-next', data);
|
|
||||||
},
|
|
||||||
PLAYER_STOP() {
|
|
||||||
ipcRenderer.send('player-stop');
|
|
||||||
},
|
|
||||||
PLAYER_VOLUME(value: number) {
|
|
||||||
ipcRenderer.send('player-volume', value);
|
|
||||||
},
|
|
||||||
RENDERER_PLAYER_CURRENT_TIME(
|
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
|
||||||
) {
|
|
||||||
ipcRenderer.on('renderer-player-current-time', cb);
|
|
||||||
},
|
|
||||||
RENDERER_PLAYER_PAUSE(cb: (event: IpcRendererEvent, data: any) => void) {
|
|
||||||
ipcRenderer.on('renderer-player-pause', cb);
|
|
||||||
},
|
|
||||||
RENDERER_PLAYER_PLAY(cb: (event: IpcRendererEvent, data: any) => void) {
|
|
||||||
ipcRenderer.on('renderer-player-play', cb);
|
|
||||||
},
|
|
||||||
RENDERER_PLAYER_SET_QUEUE_NEXT(
|
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
|
||||||
) {
|
|
||||||
ipcRenderer.on('renderer-player-set-queue-next', cb);
|
|
||||||
},
|
|
||||||
RENDERER_PLAYER_STOP(cb: (event: IpcRendererEvent, data: any) => void) {
|
|
||||||
ipcRenderer.on('renderer-player-stop', cb);
|
|
||||||
},
|
|
||||||
windowClose() {
|
|
||||||
ipcRenderer.send('window-close');
|
|
||||||
},
|
|
||||||
windowMaximize() {
|
|
||||||
ipcRenderer.send('window-maximize');
|
|
||||||
},
|
|
||||||
windowMinimize() {
|
|
||||||
ipcRenderer.send('window-minimize');
|
|
||||||
},
|
|
||||||
windowUnmaximize() {
|
|
||||||
ipcRenderer.send('window-unmaximize');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/* eslint import/prefer-default-export: off, import/no-mutable-exports: off */
|
|
||||||
import path from 'path';
|
|
||||||
import process from 'process';
|
|
||||||
import { URL } from 'url';
|
|
||||||
|
|
||||||
export let resolveHtmlPath: (htmlFileName: string) => string;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
const port = process.env.PORT || 4343;
|
|
||||||
resolveHtmlPath = (htmlFileName: string) => {
|
|
||||||
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 const isMacOS = () => {
|
|
||||||
return process.platform === 'darwin';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isWindows = () => {
|
|
||||||
return process.platform === 'win32';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isLinux = () => {
|
|
||||||
return process.platform === 'linux';
|
|
||||||
};
|
|
||||||