mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3012a8820 | |||
| 3c38c11aeb | |||
| da0625dc90 | |||
| fdb177aecf | |||
| fb98bb1e8b | |||
| f947e0b0fd | |||
| cea86d69e8 | |||
| b67f224a00 | |||
| 810a98a4b6 | |||
| c1c70ff576 | |||
| acf873f0d6 | |||
| 8de8aab23e | |||
| 3b012b7d36 | |||
| 5ed4c77e4d | |||
| 46ea46f5f7 | |||
| 827541ff8b | |||
| 2af127ac06 | |||
| c57c53972a | |||
| 2839b68e5f | |||
| 06d78bf0cf | |||
| 120e22729f | |||
| 9b210e047d | |||
| 19806f8064 | |||
| d67c1c97b7 | |||
| 0ede82dd92 | |||
| 6ac8cbe56c | |||
| f6b8460eca | |||
| a37d41813f | |||
| 528a118239 | |||
| f392fbab83 | |||
| b2ac106db4 | |||
| 57d4fa1759 | |||
| 584c07afea | |||
| 2f2bb5f16f | |||
| 817c18b40b | |||
| f01de3d1ff | |||
| 06d018883b | |||
| bcc32deda1 | |||
| c0d5c88cf2 | |||
| b6c81183e9 | |||
| f48560ef60 | |||
| 6de5d56f29 | |||
| a7ce902a16 | |||
| 42afc2b9f5 | |||
| 70c6a5e7c6 | |||
| dd44b482a2 | |||
| f41f557493 | |||
| c060df6d22 | |||
| 44013c6212 | |||
| 6a33d2c436 | |||
| c4f6886211 | |||
| e65dd130b8 | |||
| a34be65644 | |||
| 71a591792a | |||
| 2711fafb06 | |||
| 06cb95ef97 | |||
| e32ade3b54 | |||
| 9f3c6d3029 | |||
| 557eaab44c | |||
| 75b7eab2e1 | |||
| 38c9e329ac | |||
| 46d948e00f | |||
| 01d9040554 | |||
| 9319d0698a | |||
| f65ffdb412 | |||
| 516b895efe | |||
| 4d64a96f75 |
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
release/app/node_modules
|
||||
release/app/dist
|
||||
src/server/node_modules
|
||||
+10
-4
@@ -1,12 +1,18 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# https://github.com/jokeyrhyme/standard-editorconfig
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# defaults
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* TODO: Rewrite this config to ESM
|
||||
* But currently electron-builder doesn't support ESM configs
|
||||
* @see https://github.com/develar/read-config-file/issues/10
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {() => import('electron-builder').Configuration}
|
||||
* @see https://www.electron.build/configuration/configuration
|
||||
*/
|
||||
module.exports = async function () {
|
||||
const {getVersion} = await import('./version/getVersion.mjs');
|
||||
|
||||
return {
|
||||
directories: {
|
||||
output: 'dist',
|
||||
buildResources: 'buildResources',
|
||||
},
|
||||
files: ['packages/**/dist/**'],
|
||||
extraMetadata: {
|
||||
version: getVersion(),
|
||||
},
|
||||
productName: 'Feishin',
|
||||
appId: 'org.jeffvli.feishin',
|
||||
artifactName: '${productName}-${version}-${os}-${arch}.${ext}',
|
||||
win: {
|
||||
target: ['nsis', 'zip'],
|
||||
},
|
||||
publish: {
|
||||
provider: 'github',
|
||||
owner: 'jeffvli',
|
||||
repo: 'feishin',
|
||||
},
|
||||
// Specify linux target just for disabling snap compilation
|
||||
linux: {
|
||||
target: 'deb',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -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,51 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true,
|
||||
"browser": false
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
/** @see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#recommended-configs */
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"ignorePatterns": ["node_modules/**", "**/dist/**"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
/**
|
||||
* Having a semicolon helps the optimizer interpret your code correctly.
|
||||
* This avoids rare errors in optimized code.
|
||||
* @see https://twitter.com/alex_kozack/status/1364210394328408066
|
||||
*/
|
||||
"semi": ["error", "always"],
|
||||
/**
|
||||
* This will make the history of changes in the hit a little cleaner
|
||||
*/
|
||||
"comma-dangle": ["warn", "always-multiline"],
|
||||
/**
|
||||
* Just for beauty
|
||||
*/
|
||||
"quotes": [
|
||||
"warn",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+4
-12
@@ -1,12 +1,4 @@
|
||||
* 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
|
||||
.github/actions/**/*.js linguist-detectable=false
|
||||
scripts/*.js linguist-detectable=false
|
||||
*.config.js linguist-detectable=false
|
||||
* text=auto eol=lf
|
||||
|
||||
+9
-3
@@ -1,5 +1,11 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [electron-react-boilerplate, amilajack]
|
||||
patreon: amilajack
|
||||
open_collective: electron-react-boilerplate-594
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: jeffvli
|
||||
liberapay: #
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
|
||||
@@ -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
|
||||
-->
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: cawa-93
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions & Discussions
|
||||
url: https://github.com/cawa-93/vite-electron-builder/discussions/categories/q-a
|
||||
about: Use GitHub discussions for message-board style questions and discussions.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: cawa-93
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,6 +0,0 @@
|
||||
requiredHeaders:
|
||||
- Prerequisites
|
||||
- Expected Behavior
|
||||
- Current Behavior
|
||||
- Possible Solution
|
||||
- Your Environment
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":semanticCommits",
|
||||
":semanticCommitTypeAll(deps)",
|
||||
":semanticCommitScopeDisabled",
|
||||
":automergeAll",
|
||||
":automergeBranch",
|
||||
":disableDependencyDashboard",
|
||||
":pinVersions",
|
||||
":onlyNpm",
|
||||
":label(dependencies)"
|
||||
],
|
||||
"gitNoVerify": [
|
||||
"commit",
|
||||
"push"
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,47 @@
|
||||
# This workflow is the entry point for all CI processes.
|
||||
# It is from here that all other workflows are launched.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'renovate/**'
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- '!.github/workflows/ci.yml'
|
||||
- '!.github/workflows/typechecking.yml'
|
||||
- '!.github/workflows/tests.yml'
|
||||
- '!.github/workflows/release.yml'
|
||||
- '**.md'
|
||||
- .editorconfig
|
||||
- .gitignore
|
||||
- '.idea/**'
|
||||
- '.vscode/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- '!.github/workflows/ci.yml'
|
||||
- '!.github/workflows/typechecking.yml'
|
||||
- '!.github/workflows/tests.yml'
|
||||
- '!.github/workflows/release.yml'
|
||||
- '**.md'
|
||||
- .editorconfig
|
||||
- .gitignore
|
||||
- '.idea/**'
|
||||
- '.vscode/**'
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
typechecking:
|
||||
uses: ./.github/workflows/typechecking.yml
|
||||
tests:
|
||||
uses: ./.github/workflows/tests.yml
|
||||
draft_release:
|
||||
with:
|
||||
dry-run: ${{ github.event_name != 'push' || github.ref_name != 'main' }}
|
||||
needs: [ typechecking, tests ]
|
||||
uses: ./.github/workflows/release.yml
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- '**.js'
|
||||
- '**.mjs'
|
||||
- '**.cjs'
|
||||
- '**.jsx'
|
||||
- '**.ts'
|
||||
- '**.mts'
|
||||
- '**.cts'
|
||||
- '**.tsx'
|
||||
- '**.vue'
|
||||
- '**.json'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.js'
|
||||
- '**.mjs'
|
||||
- '**.cjs'
|
||||
- '**.jsx'
|
||||
- '**.ts'
|
||||
- '**.mts'
|
||||
- '**.cts'
|
||||
- '**.tsx'
|
||||
- '**.vue'
|
||||
- '**.json'
|
||||
|
||||
concurrency:
|
||||
group: lint-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
||||
- run: npm run lint --if-present
|
||||
|
||||
# This job just check code style for in-template contributions.
|
||||
code-style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
- run: npm i prettier
|
||||
- run: npx prettier --check "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,json}"
|
||||
@@ -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
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Release
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
dry-run:
|
||||
description: 'Compiles the app but not upload artifacts to distribution server'
|
||||
default: false
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
|
||||
jobs:
|
||||
draft_release:
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ macos-latest, ubuntu-latest, windows-latest ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- name: Compile artifacts ${{ inputs.dry-run && '' || 'and upload them to github release' }}
|
||||
# I use this action because it is capable of retrying multiple times if there are any issues with the distribution server
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
timeout_minutes: 15
|
||||
max_attempts: 3
|
||||
retry_on: error
|
||||
shell: 'bash'
|
||||
command: npx --no-install electron-builder --config .electron-builder.config.js --publish ${{ inputs.dry-run && 'never' || 'always' }}
|
||||
env:
|
||||
# Code Signing params
|
||||
# See https://www.electron.build/code-signing
|
||||
# CSC_LINK: ''
|
||||
# CSC_KEY_PASSWORD: ''
|
||||
# Publishing artifacts
|
||||
GH_TOKEN: ${{ secrets.github_token }} # GitHub token, automatically provided (No need to define this secret in the repo settings)
|
||||
@@ -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
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Tests
|
||||
on: [ workflow_call ]
|
||||
|
||||
concurrency:
|
||||
group: tests-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
unit:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ windows-latest, ubuntu-latest, macos-latest ]
|
||||
package: [ main, preload, renderer ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
- run: npm run test:${{ matrix.package }} --if-present
|
||||
|
||||
e2e:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ windows-latest, ubuntu-latest, macos-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
||||
# I ran into problems trying to run an electron window in ubuntu due to a missing graphics server.
|
||||
# That's why this special command for Ubuntu is here
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e --if-present
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- run: npm run test:e2e --if-present
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Typechecking
|
||||
on: [ workflow_call ]
|
||||
|
||||
concurrency:
|
||||
group: typechecking-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
typescript:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16 # Need for npm >=7.7
|
||||
cache: 'npm'
|
||||
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
||||
- run: npm run typecheck --if-present
|
||||
+53
-26
@@ -1,31 +1,58 @@
|
||||
# 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
|
||||
dist
|
||||
*.local
|
||||
thumbs.db
|
||||
|
||||
release/app/dist
|
||||
release/build
|
||||
.erb/dll
|
||||
.eslintcache
|
||||
.browserslistrc
|
||||
.electron-vendors.cache.json
|
||||
|
||||
.idea
|
||||
npm-debug.log.*
|
||||
*.css.d.ts
|
||||
*.sass.d.ts
|
||||
*.scss.d.ts
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
.env*
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
/.idea/csv-plugin.xml
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
Generated
+65
@@ -0,0 +1,65 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_ATTRIBUTE_WRAP" value="4" />
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="HTML_ENFORCE_QUOTES" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_TYPE_BRACES" value="false" />
|
||||
</JSCodeStyleSettings>
|
||||
<JSON>
|
||||
<option name="OBJECT_WRAPPING" value="5" />
|
||||
<option name="ARRAY_WRAPPING" value="5" />
|
||||
</JSON>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_TYPE_BRACES" value="false" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="UNIFORM_INDENT" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
Generated
+5
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
Generated
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
|
||||
<serverData>
|
||||
<paths name="ihappymama-aliexpress">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
<paths name="iosico.com">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
<paths name="somespeed.com">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
</serverData>
|
||||
</component>
|
||||
</project>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EslintConfiguration">
|
||||
<work-dir-patterns value="src/**/*.{ts,vue} {bin,config}/**/*.js" />
|
||||
<option name="fix-on-save" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JsonSchemaMappingsProjectConfiguration">
|
||||
<state>
|
||||
<map>
|
||||
<entry key="GitHub Workflow">
|
||||
<value>
|
||||
<SchemaInfo>
|
||||
<option name="name" value="GitHub Workflow" />
|
||||
<option name="relativePathToSchema" value="https://json.schemastore.org/github-workflow.json" />
|
||||
<option name="applicationDefined" value="true" />
|
||||
<option name="patterns">
|
||||
<list>
|
||||
<Item>
|
||||
<option name="path" value=".github/workflows/release.yml" />
|
||||
</Item>
|
||||
<Item>
|
||||
<option name="path" value=".github/workflows/ci.yml" />
|
||||
</Item>
|
||||
</list>
|
||||
</option>
|
||||
</SchemaInfo>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</state>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/vite-electron-builder.iml" filepath="$PROJECT_DIR$/.idea/vite-electron-builder.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myRunOnSave" value="true" />
|
||||
<option name="myRunOnReformat" value="true" />
|
||||
<option name="myFilesPattern" value="{**/*,*}.{js,mjs,cjs,ts,mts,cts,vue,json}" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/packages/main/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/packages/preload/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/packages/renderer/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/renderer/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/main/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/preload/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/preload/node_modules" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/main/node_modules" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebResourcesPaths">
|
||||
<contentEntries>
|
||||
<entry url="file://$PROJECT_DIR$">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/packages/renderer/assets" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
</contentEntries>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
import {resolve, sep} from 'path';
|
||||
|
||||
export default {
|
||||
'*.{js,mjs,cjs,ts,mts,cts,vue}': 'eslint --cache --fix',
|
||||
|
||||
/**
|
||||
* Run typechecking if any type-sensitive files or project dependencies was changed
|
||||
* @param {string[]} filenames
|
||||
* @return {string[]}
|
||||
*/
|
||||
'{package-lock.json,packages/**/{*.ts,*.vue,tsconfig.json}}': ({filenames}) => {
|
||||
// if dependencies was changed run type checking for all packages
|
||||
if (filenames.some(f => f.endsWith('package-lock.json'))) {
|
||||
return ['npm run typecheck --if-present'];
|
||||
}
|
||||
|
||||
// else run type checking for staged packages
|
||||
const fileNameToPackageName = filename =>
|
||||
filename.replace(resolve(process.cwd(), 'packages') + sep, '').split(sep)[0];
|
||||
return [...new Set(filenames.map(fileNameToPackageName))].map(
|
||||
p => `npm run typecheck:${p} --if-present`,
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/*.svg
|
||||
|
||||
package.json
|
||||
package-lock.json
|
||||
.electron-vendors.cache.json
|
||||
|
||||
.github
|
||||
.idea
|
||||
+16
-4
@@ -1,8 +1,20 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"arrowParens": "always"
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.css", "**/*.scss", "**/*.html"],
|
||||
"options": {
|
||||
"singleQuote": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "never",
|
||||
"htmlWhitespaceSensitivity": "strict",
|
||||
"endOfLine": "lf",
|
||||
"singleAttributePerLine": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"pre-commit": "npx nano-staged"
|
||||
}
|
||||
+4
-4
@@ -17,11 +17,11 @@
|
||||
"empty-line-between-groups": false
|
||||
}
|
||||
],
|
||||
"string-quotes": "single",
|
||||
"declaration-block-no-redundant-longhand-properties": null,
|
||||
"selector-class-pattern": null,
|
||||
"selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
|
||||
"selector-type-no-unknown": [
|
||||
true,
|
||||
{ "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }
|
||||
],
|
||||
"selector-type-no-unknown": [true, { "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }],
|
||||
"value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }],
|
||||
"declaration-colon-newline-after": null
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"stylelint.vscode-stylelint",
|
||||
"esbenp.prettier-vscode"
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-vscode.vscode-typescript-next"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+4
-21
@@ -2,29 +2,12 @@
|
||||
"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": [
|
||||
{
|
||||
"name": "Electron: All",
|
||||
"configurations": ["Electron: Main", "Electron: Renderer"]
|
||||
"name": "Debug Main Process",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${workspaceFolder}\\scripts\\watch.mjs",
|
||||
"autoAttachChildProcesses": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+6
-28
@@ -1,30 +1,8 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"stylelint.validate": ["css", "less", "postcss", "typescript", "typescriptreact", "scss"],
|
||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true
|
||||
}
|
||||
|
||||
Vendored
-25
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"label": "Start Webpack Dev",
|
||||
"script": "start:renderer",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "custom",
|
||||
"pattern": {
|
||||
"regexp": "____________"
|
||||
},
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "Compiling\\.\\.\\.$",
|
||||
"endsPattern": "(Compiled successfully|Failed to compile)\\.$"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
-586
@@ -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
|
||||
-42
@@ -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,3 +1,19 @@
|
||||
Copyright (C) 2022 jeffvli
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
@@ -70,7 +86,7 @@ modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
1. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
@@ -618,57 +634,4 @@ an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,129 +1,305 @@
|
||||
<img src="assets/icon.png" alt="sonixd logo" title="sonixd" align="right" height="60px" />
|
||||
# Vite Electron Builder Boilerplate
|
||||
|
||||
# Sonixd
|
||||
|
||||
<a href="https://github.com/jeffvli/sonixd/releases">
|
||||
<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>
|
||||
[](https://stand-with-ukraine.pp.ua)
|
||||
|
||||
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>.
|
||||
[](https://github.com/cawa-93/vite-electron-builder/issues?q=label%3A%22help+wanted%22+is%3Aopen+is%3Aissue)
|
||||
[](https://nodejs.org/about/releases/)
|
||||
[](https://github.com/npm/cli/releases)
|
||||
|
||||
- [**Usage documentation & FAQ**](https://github.com/jeffvli/sonixd/discussions/15)
|
||||
- [**Theming documentation**](https://github.com/jeffvli/sonixd/discussions/61)
|
||||
> Vite+Electron = 🔥
|
||||
|
||||
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)
|
||||
This is a template for secure electron applications. Written following the latest safety requirements, recommendations
|
||||
and best practices.
|
||||
|
||||
### [Demo Sonixd using Navidrome](https://github.com/jeffvli/sonixd/discussions/244)
|
||||
Under the hood is [Vite] — A next-generation blazing fast bundler, and [electron-builder] for packaging.
|
||||
|
||||
## Get started
|
||||
|
||||
Follow these steps to get started with the template:
|
||||
|
||||
1. Click the **[Use this template](https://github.com/cawa-93/vite-electron-builder/generate)** button (you must be
|
||||
logged in) or just clone this repo.
|
||||
2. If you want to use another package manager don't forget to edit [`.github/workflows`](/.github/workflows) -- it
|
||||
uses `npm` by default.
|
||||
|
||||
That's all you need. 😉
|
||||
|
||||
**Note**: This template uses npm v7 feature — [**Installing Peer Dependencies
|
||||
Automatically**](https://github.com/npm/rfcs/blob/latest/implemented/0025-install-peer-deps.md). If you are using a
|
||||
different package manager, you may need to install some peerDependencies manually.
|
||||
|
||||
**Note**: Find more useful forks [here](https://github.com/cawa-93/vite-electron-builder/discussions/categories/forks).
|
||||
|
||||
## 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
|
||||
### Electron [][electron]
|
||||
|
||||
<h5>* Gapless playback is artifically created using the crossfading players so it may not be perfect, YMMV.</h5>
|
||||
- This template uses the latest electron version with all the latest security patches.
|
||||
- The architecture of the application is built according to the
|
||||
security [guides](https://www.electronjs.org/docs/tutorial/security) and best practices.
|
||||
- The latest version of the [electron-builder] is used to package the application.
|
||||
|
||||
## Screenshots
|
||||
### Vite [][vite]
|
||||
|
||||
<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>
|
||||
- [Vite] is used to bundle all source codes. It's an extremely fast bundler, that has a vast array of amazing features.
|
||||
You can learn more about how it is arranged in [this](https://www.youtube.com/watch?v=xXrhg26VCSc) video.
|
||||
- Vite [supports](https://vitejs.dev/guide/env-and-mode.html) reading `.env` files. You can also specify the types of
|
||||
your environment variables in [`types/env.d.ts`](types/env.d.ts).
|
||||
- Automatic hot-reloads for the `Main` and `Renderer` processes.
|
||||
|
||||
## Install
|
||||
Vite provides many useful features, such as: `TypeScript`, `TSX/JSX`, `CSS/JSON Importing`, `CSS Modules`
|
||||
, `Web Assembly` and much more.
|
||||
|
||||
You can install sonixd by downloading the [latest release](https://github.com/jeffvli/sonixd/releases) for your specified operating system.
|
||||
[See all Vite features](https://vitejs.dev/guide/features.html).
|
||||
|
||||
---
|
||||
### TypeScript [][typescript] (optional)
|
||||
|
||||
### Windows
|
||||
- The latest version of TypeScript is used for all the source code.
|
||||
- **Vite** supports TypeScript out of the box. However, it does not support type checking.
|
||||
- Code formatting rules follow the latest TypeScript recommendations and best practices thanks
|
||||
to [@typescript-eslint/eslint-plugin](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin).
|
||||
|
||||
If you prefer not to download the release binary, you can install using `winget`.
|
||||
**[See this discussion](https://github.com/cawa-93/vite-electron-builder/discussions/339)** if you want completely
|
||||
remove TypeScript.
|
||||
|
||||
Using your favorite terminal (cmd/pwsh):
|
||||
### Vue [][vue] (optional)
|
||||
|
||||
```
|
||||
winget install sonixd
|
||||
- By default, web pages are built using [Vue]. However, you can easily change that. Or not use additional frameworks at
|
||||
all.
|
||||
- Code formatting rules follow the latest Vue recommendations and best practices thanks to [eslint-plugin-vue].
|
||||
|
||||
See [examples of web pages for different frameworks](https://github.com/vitejs/vite/tree/main/packages/create-vite).
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- The configured workflow will check the types for each push and PR.
|
||||
- The configured workflow will check the code style for each push and PR.
|
||||
- **Automatic tests**
|
||||
used [Vitest ][vitest]
|
||||
-- A blazing fast test framework powered by Vite.
|
||||
- Unit tests are placed within each package and are ran separately.
|
||||
- End-to-end tests are placed in the root [`tests`](tests) directory and use [playwright].
|
||||
|
||||

|
||||
|
||||
|
||||
### Publishing
|
||||
|
||||
- Each time you push changes to the `main` branch, the [`release`](.github/workflows/release.yml) workflow starts, which creates a new draft release. For each next commit will be created and replaced artifacts. That way you will always have draft with latest artifacts, and the release can be published once it is ready.
|
||||
- Code signing supported. See [`release` workflow](.github/workflows/release.yml).
|
||||
- **Auto-update is supported**. After the release is published, all client applications will download the new version
|
||||
and install updates silently.
|
||||
|
||||
This template configured for GitHub, but electron-builder supports multiple auto-update servers. See [docs](https://www.electron.build/configuration/publish).
|
||||
|
||||
## How it works
|
||||
|
||||
The template requires a minimum amount [dependencies](package.json). Only **Vite** is used for building, nothing more.
|
||||
|
||||
### Project Structure
|
||||
|
||||
The structure of this template is very similar to the structure of a monorepo.
|
||||
|
||||
```mermaid
|
||||
flowchart TB;
|
||||
|
||||
packages/preload <-. IPC Messages .-> packages/main
|
||||
|
||||
subgraph packages/main
|
||||
M[index.ts] --> EM[Electron Main Process Modules]
|
||||
M --> N2[Node.js API]
|
||||
end
|
||||
|
||||
|
||||
subgraph packages/preload
|
||||
P[index.ts] --> N[Node.js API]
|
||||
P --> ED[External dependencies]
|
||||
P --> ER[Electron Renderer Process Modules]
|
||||
end
|
||||
|
||||
|
||||
subgraph packages/renderer
|
||||
R[index.html] --> W[Web API]
|
||||
R --> BD[Bundled dependencies]
|
||||
R --> F[Web Frameforks]
|
||||
end
|
||||
|
||||
packages/renderer -- Call Exposed API --> P
|
||||
```
|
||||
|
||||
---
|
||||
The entire source code of the project is divided into three modules (packages) that are each bundled independently:
|
||||
|
||||
### Arch Linux
|
||||
- [`packages/renderer`](packages/renderer). Responsible for the contents of the application window. In fact, it is a
|
||||
regular web application. In developer mode, you can even open it in a browser. The development and build process is
|
||||
the same as for classic web applications. Access to low-level API electrons or Node.js is done through the _preload_
|
||||
layer.
|
||||
- [`packages/preload`](packages/preload). Acts as an intermediate bridge between the _renderer_ process and the API
|
||||
exposed by electron and Node.js. Runs in an _isolated browser context_, but has direct access to the full Node.js
|
||||
functionality.
|
||||
See [Checklist: Security Recommendations](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content)
|
||||
.
|
||||
- [`packages/main`](packages/main)
|
||||
Electron [**main script**](https://www.electronjs.org/docs/tutorial/quick-start#create-the-main-script-file). This is
|
||||
the main process that powers the application. It manages creating and handling the spawned BrowserWindow, setting and
|
||||
enforcing secure permissions and request handlers. You can also configure it to do much more as per your need, such
|
||||
as: logging, reporting statistics and health status among others.
|
||||
|
||||
There is an AUR package of the latest AppImage release available [here](https://aur.archlinux.org/packages/sonixd-appimage).
|
||||
### Build web resources
|
||||
|
||||
To install it you can use your favourite AUR package manager and install the package: `sonixd-appimage`
|
||||
The `main` and `preload` packages are built in [library mode](https://vitejs.dev/guide/build.html#library-mode) as it is
|
||||
simple javascript.
|
||||
The `renderer` package builds as a regular web app.
|
||||
|
||||
For example using `yay`:
|
||||
### Compile App
|
||||
|
||||
```
|
||||
yay -S sonixd-appimage
|
||||
The next step is to package a ready to distribute Electron app for macOS, Windows and Linux with "auto update" support
|
||||
out of the box.
|
||||
|
||||
To do this, use [electron-builder]:
|
||||
|
||||
- Using the npm script `compile`: This script is configured to compile the application as quickly as possible. It is not
|
||||
ready for distribution, it is compiled only for the current platform and is used for debugging.
|
||||
- Using GitHub Actions: The application is compiled for any platform and ready-to-distribute files are automatically
|
||||
added as a draft to the GitHub releases page.
|
||||
|
||||
### Working with dependencies
|
||||
|
||||
Because the `renderer` works and builds like a _regular web application_, you can only use dependencies that support the
|
||||
browser or compile to a browser-friendly format.
|
||||
|
||||
This means that in the `renderer` you are free to use any frontend dependencies such as Vue, React, lodash, axios and so
|
||||
on.However, you _CANNOT_ use any native Node.js APIs, such as, `systeminformation`. These APIs are _only_ available in
|
||||
a Node.js runtime environment and will cause your application to crash if used in the `renderer` layer. Instead, if you
|
||||
need access to Node.js runtime APIs in your frontend, export a function form the `preload` package.
|
||||
|
||||
All dependencies that require Node.js api can be used in
|
||||
the [`preload` script](https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts).
|
||||
|
||||
#### Expose in main world
|
||||
Here is an example. Let's say you need to read some data from the file system or database in the renderer.
|
||||
|
||||
In the preload context, create a function that reads and returns data. To make the function announced in the preload
|
||||
available in the render, you usually need to call
|
||||
the [`electron.contextBridge.exposeInMainWorld`](https://www.electronjs.org/ru/docs/latest/api/context-bridge). However,
|
||||
this template uses the [unplugin-auto-expose](https://github.com/cawa-93/unplugin-auto-expose) plugin, so you just need
|
||||
to export the method from the preload. The `exposeInMainWorld` will be called automatically.
|
||||
|
||||
```ts
|
||||
// preload/index.ts
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
// Encapsulate types if you use typescript
|
||||
interface UserData {
|
||||
prop: string
|
||||
}
|
||||
|
||||
// Encapsulate all node.js api
|
||||
// Everything you exported from preload/index.ts may be called in renderer
|
||||
export function getUserData(): Promise<UserData> {
|
||||
return readFile('/path/to/file/in/user/filesystem.json', {encoding:'utf8'}).then(JSON.parse);
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
Now you can import and call the method in renderer
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
```ts
|
||||
// renderer/anywere/component.ts
|
||||
import { getUserData } from '#preload'
|
||||
const userData = await getUserData()
|
||||
```
|
||||
|
||||
Start the app in the `dev` environment:
|
||||
[Read more about Security Considerations](https://www.electronjs.org/docs/tutorial/context-isolation#security-considerations).
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
### Working with Electron API
|
||||
|
||||
Although the preload has access to all of Node.js's API, it **still runs in the BrowserWindow context**, so a limited
|
||||
electron modules are available in it. Check the [electron docs](https://www.electronjs.org/ru/docs/latest/api/clipboard)
|
||||
for full list of available methods.
|
||||
|
||||
All other electron methods can be invoked in the `main`.
|
||||
|
||||
As a result, the architecture of interaction between all modules is as follows:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
renderer->>+preload: Read data from file system
|
||||
preload->>-renderer: Data
|
||||
renderer->>preload: Maximize window
|
||||
activate preload
|
||||
preload-->>main: Invoke IPC command
|
||||
activate main
|
||||
main-->>preload: IPC response
|
||||
deactivate main
|
||||
preload->>renderer: Window maximized
|
||||
deactivate preload
|
||||
```
|
||||
|
||||
To package apps for the local platform:
|
||||
[Read more about Inter-Process Communication](https://www.electronjs.org/docs/latest/tutorial/ipc)
|
||||
|
||||
```bash
|
||||
yarn package
|
||||
### Modes and Environment Variables
|
||||
|
||||
All environment variables are set as part of the `import.meta`, so you can access them vie the following
|
||||
way: `import.meta.env`.
|
||||
|
||||
If you are using TypeScript and want to get code completion you must add all the environment variables to
|
||||
the [`ImportMetaEnv` in `types/env.d.ts`](types/env.d.ts).
|
||||
|
||||
The mode option is used to specify the value of `import.meta.env.MODE` and the corresponding environment variables files
|
||||
that need to be loaded.
|
||||
|
||||
By default, there are two modes:
|
||||
|
||||
- `production` is used by default
|
||||
- `development` is used by `npm run watch` script
|
||||
|
||||
When running the build script, the environment variables are loaded from the following files in your project root:
|
||||
|
||||
```
|
||||
.env # loaded in all cases
|
||||
.env.local # loaded in all cases, ignored by git
|
||||
.env.[mode] # only loaded in specified env mode
|
||||
.env.[mode].local # only loaded in specified env mode, ignored by git
|
||||
```
|
||||
|
||||
If you receive errors while packaging the application, try upgrading/downgrading your Node version (tested on v14.18.0).
|
||||
To prevent accidentally leaking env variables to the client, only variables prefixed with `VITE_` are exposed to your
|
||||
Vite-processed code.
|
||||
|
||||
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).
|
||||
For example let's take the following `.env` file:
|
||||
|
||||
If your devtools extensions are failing to run/install, check troubleshooting steps [here](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/2788).
|
||||
```
|
||||
DB_PASSWORD=foobar
|
||||
VITE_SOME_KEY=123
|
||||
```
|
||||
|
||||
## License
|
||||
Only `VITE_SOME_KEY` will be exposed as `import.meta.env.VITE_SOME_KEY` to your client source code, but `DB_PASSWORD`
|
||||
will not.
|
||||
|
||||
[GNU General Public License v3.0 ©](https://github.com/jeffvli/sonixd/blob/main/LICENSE)
|
||||
## Contribution
|
||||
|
||||
See [Contributing Guide](contributing.md).
|
||||
|
||||
|
||||
[vite]: https://github.com/vitejs/vite/
|
||||
|
||||
[electron]: https://github.com/electron/electron
|
||||
|
||||
[electron-builder]: https://github.com/electron-userland/electron-builder
|
||||
|
||||
[vue]: https://github.com/vuejs/vue-next
|
||||
|
||||
[vue-router]: https://github.com/vuejs/vue-router-next/
|
||||
|
||||
[typescript]: https://github.com/microsoft/TypeScript/
|
||||
|
||||
[playwright]: https://playwright.dev
|
||||
|
||||
[vitest]: https://vitest.dev
|
||||
|
||||
[vue-tsc]: https://github.com/johnsoncodehk/vue-tsc
|
||||
|
||||
[eslint-plugin-vue]: https://github.com/vuejs/eslint-plugin-vue
|
||||
|
||||
[cawa-93-github]: https://github.com/cawa-93/
|
||||
|
||||
[cawa-93-sponsor]: https://www.patreon.com/Kozack/
|
||||
|
||||
Vendored
-31
@@ -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>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@@ -0,0 +1,34 @@
|
||||
# Contributing
|
||||
|
||||
First and foremost, thank you! We appreciate that you want to contribute to vite-electron-builder, your time is
|
||||
valuable, and your contributions mean a lot to us.
|
||||
|
||||
## Issues
|
||||
|
||||
Do not create issues about bumping dependencies unless a bug has been identified, and you can demonstrate that it
|
||||
effects this library.
|
||||
|
||||
**Help us to help you**
|
||||
|
||||
Remember that we’re here to help, but not to make guesses about what you need help with:
|
||||
|
||||
- Whatever bug or issue you're experiencing, assume that it will not be as obvious to the maintainers as it is to you.
|
||||
- Spell it out completely. Keep in mind that maintainers need to think about _all potential use cases_ of a library.
|
||||
It's important that you explain how you're using a library so that maintainers can make that connection and solve the
|
||||
issue.
|
||||
|
||||
_It can't be understated how frustrating and draining it can be to maintainers to have to ask clarifying questions on
|
||||
the most basic things, before it's even possible to start debugging. Please try to make the best use of everyone's time
|
||||
involved, including yourself, by providing this information up front._
|
||||
|
||||
## Repo Setup
|
||||
|
||||
The package manager used to install and link dependencies must be npm v7 or later.
|
||||
|
||||
1. Clone repo
|
||||
1. `npm run watch` start electron app in watch mode.
|
||||
1. `npm run compile` build app but for local debugging only.
|
||||
1. `npm run lint` lint your code.
|
||||
1. `npm run typecheck` Run typescript check.
|
||||
1. `npm run test` Run app test.
|
||||
1. `npm run format` Reformat all codebase to project code style.
|
||||
@@ -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,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Alex Kozack
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Generated
+7667
-18428
File diff suppressed because it is too large
Load Diff
+92
-273
@@ -1,200 +1,52 @@
|
||||
{
|
||||
"name": "sonixd",
|
||||
"productName": "Sonixd",
|
||||
"description": "A full-featured Subsonic/Jellyfin compatible music player",
|
||||
"scripts": {
|
||||
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
|
||||
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
|
||||
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
|
||||
"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"
|
||||
},
|
||||
"name": "feishin",
|
||||
"description": "A full featured Jellyfin/Subsonic/Navidrome music player.",
|
||||
"version": "1.0.0-alpha1",
|
||||
"author": {
|
||||
"name": "jeffvli",
|
||||
"url": "https://github.com/jeffvli/"
|
||||
"email": "jeffvictorli@gmail.com",
|
||||
"url": "https://github.com/jeffvli"
|
||||
},
|
||||
"contributors": [],
|
||||
"license": "GPL-3.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jeffvli/sonixd/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"subsonic",
|
||||
"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"
|
||||
]
|
||||
"main": "packages/main/dist/index.cjs",
|
||||
"scripts": {
|
||||
"build": "npm run build:main && npm run build:preload && npm run build:renderer",
|
||||
"build:main": "cd ./packages/main && vite build",
|
||||
"build:preload": "cd ./packages/preload && vite build",
|
||||
"build:renderer": "cd ./packages/renderer && vite build",
|
||||
"compile": "cross-env MODE=production npm run build && electron-builder build --config .electron-builder.config.js --dir",
|
||||
"test": "npm run test:main && npm run test:preload && npm run test:renderer && npm run test:e2e",
|
||||
"test:e2e": "npm run build && vitest run",
|
||||
"test:main": "vitest run -r packages/main --passWithNoTests",
|
||||
"test:preload": "vitest run -r packages/preload --passWithNoTests",
|
||||
"test:renderer": "vitest run -r packages/renderer --passWithNoTests",
|
||||
"watch": "node scripts/watch.mjs",
|
||||
"lint": "eslint . --ext js,mjs,cjs,ts,mts,cts,tsx",
|
||||
"typecheck:main": "tsc --noEmit -p packages/main/tsconfig.json",
|
||||
"typecheck:preload": "tsc --noEmit -p packages/preload/tsconfig.json",
|
||||
"typecheck:renderer": "tsc --noEmit -p packages/renderer/tsconfig.json",
|
||||
"typecheck": "npm run typecheck:main && npm run typecheck:preload && npm run typecheck:renderer",
|
||||
"postinstall": "cross-env ELECTRON_RUN_AS_NODE=1 electron scripts/update-electron-vendors.mjs",
|
||||
"format": "npx prettier --write \"**/*.{js,mjs,cjs,ts,mts,cts,tsx,json}\""
|
||||
},
|
||||
"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/electron-localshortcut": "^3.1.0",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@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/node": "18.11.10",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@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",
|
||||
"@types/styled-components": "^5.1.26",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.1",
|
||||
"@typescript-eslint/parser": "^5.45.1",
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "22.0.0",
|
||||
"electron-builder": "23.6.0",
|
||||
"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": "8.29.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-erb": "^4.0.3",
|
||||
"eslint-import-resolver-typescript": "^2.7.1",
|
||||
@@ -202,108 +54,75 @@
|
||||
"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-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.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",
|
||||
"happy-dom": "7.7.2",
|
||||
"nano-staged": "0.8.0",
|
||||
"playwright": "1.27.1",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"sass": "^1.56.1",
|
||||
"simple-git-hooks": "2.8.1",
|
||||
"stylelint": "^14.16.0",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-standard-scss": "^4.0.0",
|
||||
"stylelint-config-standard-scss": "^6.1.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"
|
||||
"typescript": "4.9.3",
|
||||
"unplugin-auto-expose": "0.0.4",
|
||||
"vite": "3.2.4",
|
||||
"vitest": "0.25.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jellyfin/client-axios": "^10.7.8",
|
||||
"@mantine/core": "^5.0.0",
|
||||
"@mantine/form": "^5.0.0",
|
||||
"@mantine/hooks": "^5.0.0",
|
||||
"axios": "^0.26.1",
|
||||
"electron-debug": "^3.2.0",
|
||||
"electron-log": "^4.4.6",
|
||||
"electron-updater": "^4.6.5",
|
||||
"@ag-grid-community/client-side-row-model": "^28.2.1",
|
||||
"@ag-grid-community/core": "^28.2.1",
|
||||
"@ag-grid-community/react": "^28.2.1",
|
||||
"@ag-grid-community/styles": "^28.2.1",
|
||||
"@mantine/core": "^5.9.2",
|
||||
"@mantine/dates": "^5.9.2",
|
||||
"@mantine/dropzone": "^5.9.2",
|
||||
"@mantine/form": "^5.9.2",
|
||||
"@mantine/hooks": "^5.9.2",
|
||||
"@mantine/modals": "^5.9.2",
|
||||
"@mantine/notifications": "^5.9.2",
|
||||
"@mantine/spotlight": "^5.9.2",
|
||||
"@tanstack/react-query": "^4.19.1",
|
||||
"@tanstack/react-query-devtools": "^4.19.1",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"electron-localshortcut": "^3.2.1",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "5.3.0",
|
||||
"fast-average-color": "^9.1.1",
|
||||
"format-duration": "^2.0.0",
|
||||
"framer-motion": "^6.4.2",
|
||||
"history": "^5.3.0",
|
||||
"i18next": "^21.6.16",
|
||||
"immer": "^9.0.15",
|
||||
"framer-motion": "^7.6.19",
|
||||
"immer": "^9.0.16",
|
||||
"is-electron": "^2.2.1",
|
||||
"ky": "^0.32.2",
|
||||
"ky-universal": "^0.11.0",
|
||||
"lodash": "^4.17.21",
|
||||
"md5": "^2.3.0",
|
||||
"nanoid": "^3.3.3",
|
||||
"net": "^1.0.2",
|
||||
"memoize-one": "^6.0.0",
|
||||
"nanoid": "^4.0.0",
|
||||
"node-mpv": "^2.0.0-beta.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"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-icons": "^4.7.1",
|
||||
"react-player": "^2.11.0",
|
||||
"react-router": "^6.4.4",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-simple-img": "^3.0.0",
|
||||
"react-slider": "^2.0.4",
|
||||
"react-virtualized-auto-sizer": "^1.0.7",
|
||||
"react-window": "^1.8.8",
|
||||
"react-window-infinite-loader": "^1.0.8",
|
||||
"styled-components": "^5.3.5",
|
||||
"tabler-icons-react": "^1.46.0",
|
||||
"zustand": "^4.0.0-rc.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"styled-components": "^5"
|
||||
},
|
||||
"devEngines": {
|
||||
"node": ">=14.x",
|
||||
"npm": ">=7.x"
|
||||
},
|
||||
"browserslist": [],
|
||||
"prettier": {
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
".prettierrc",
|
||||
".eslintrc"
|
||||
],
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
}
|
||||
],
|
||||
"singleQuote": true
|
||||
"styled-components": "^5.3.6",
|
||||
"zod": "^3.19.1",
|
||||
"zustand": "^4.1.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import './player';
|
||||
import './media-keys';
|
||||
import './settings';
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import { globalShortcut } from 'electron';
|
||||
|
||||
export const enableMediaKeys = (window: BrowserWindow | null) => {
|
||||
globalShortcut.register('MediaStop', () => {
|
||||
window?.webContents.send('renderer-player-stop');
|
||||
});
|
||||
|
||||
globalShortcut.register('MediaPlayPause', () => {
|
||||
window?.webContents.send('renderer-player-play-pause');
|
||||
});
|
||||
|
||||
globalShortcut.register('MediaNextTrack', () => {
|
||||
window?.webContents.send('renderer-player-next');
|
||||
});
|
||||
|
||||
globalShortcut.register('MediaPreviousTrack', () => {
|
||||
window?.webContents.send('renderer-player-previous');
|
||||
});
|
||||
};
|
||||
|
||||
export const disableMediaKeys = () => {
|
||||
globalShortcut.unregister('MediaStop');
|
||||
globalShortcut.unregister('MediaPlayPause');
|
||||
globalShortcut.unregister('MediaNextTrack');
|
||||
globalShortcut.unregister('MediaPreviousTrack');
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import MpvAPI from 'node-mpv';
|
||||
import { store } from '../settings';
|
||||
import uniq from 'lodash/uniq';
|
||||
import type { PlayerData } from '../../../../../renderer/src/store/player.store';
|
||||
import { getBrowserWindow } from '/@/mainWindow';
|
||||
|
||||
declare module 'node-mpv';
|
||||
|
||||
const BINARY_PATH = store.get('mpv_path') as string | undefined;
|
||||
const MPV_PARAMETERS = store.get('mpv_parameters') as Array<string> | undefined;
|
||||
const DEFAULT_MPV_PARAMETERS = () => {
|
||||
const parameters = [];
|
||||
if (
|
||||
!MPV_PARAMETERS?.includes('--gapless-audio=weak') ||
|
||||
!MPV_PARAMETERS?.includes('--gapless-audio=no') ||
|
||||
!MPV_PARAMETERS?.includes('--gapless-audio=yes') ||
|
||||
!MPV_PARAMETERS?.includes('--gapless-audio')
|
||||
) {
|
||||
parameters.push('--gapless-audio=yes');
|
||||
}
|
||||
|
||||
if (
|
||||
!MPV_PARAMETERS?.includes('--prefetch-playlist=no') ||
|
||||
!MPV_PARAMETERS?.includes('--prefetch-playlist=yes') ||
|
||||
!MPV_PARAMETERS?.includes('--prefetch-playlist')
|
||||
) {
|
||||
parameters.push('--prefetch-playlist=yes');
|
||||
}
|
||||
|
||||
return parameters;
|
||||
};
|
||||
|
||||
const mpv = new MpvAPI(
|
||||
{
|
||||
audio_only: true,
|
||||
auto_restart: true,
|
||||
binary: BINARY_PATH || '',
|
||||
time_update: 1,
|
||||
},
|
||||
MPV_PARAMETERS
|
||||
? uniq([...DEFAULT_MPV_PARAMETERS(), ...MPV_PARAMETERS])
|
||||
: DEFAULT_MPV_PARAMETERS(),
|
||||
);
|
||||
|
||||
mpv
|
||||
.start()
|
||||
.then(async () => {
|
||||
// await mpv.load('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', 'replace');
|
||||
// await mpv.play();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('error', error);
|
||||
});
|
||||
|
||||
mpv.on('status', (status) => {
|
||||
if (status.property === 'playlist-pos') {
|
||||
if (status.value !== 0) {
|
||||
getBrowserWindow()?.webContents.send('renderer-player-auto-next');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Automatically updates the play button when the player is playing
|
||||
mpv.on('resumed', () => {
|
||||
getBrowserWindow()?.webContents.send('renderer-player-play');
|
||||
});
|
||||
|
||||
// Automatically updates the play button when the player is stopped
|
||||
mpv.on('stopped', () => {
|
||||
getBrowserWindow()?.webContents.send('renderer-player-stop');
|
||||
});
|
||||
|
||||
// Automatically updates the play button when the player is paused
|
||||
mpv.on('paused', () => {
|
||||
getBrowserWindow()?.webContents.send('renderer-player-pause');
|
||||
});
|
||||
|
||||
mpv.on('quit', () => {
|
||||
console.log('mpv quit');
|
||||
});
|
||||
|
||||
// Event output every interval set by time_update, used to update the current time
|
||||
mpv.on('timeposition', (time: number) => {
|
||||
getBrowserWindow()?.webContents.send('renderer-player-current-time', time);
|
||||
});
|
||||
|
||||
// Starts the player
|
||||
ipcMain.on('player-play', async () => {
|
||||
await mpv.play();
|
||||
});
|
||||
|
||||
// Pauses the player
|
||||
ipcMain.on('player-pause', async () => {
|
||||
await mpv.pause();
|
||||
});
|
||||
|
||||
// Stops the player
|
||||
ipcMain.on('player-stop', async () => {
|
||||
await mpv.stop();
|
||||
});
|
||||
|
||||
// Goes to the next track in the playlist
|
||||
ipcMain.on('player-next', async () => {
|
||||
await mpv.next();
|
||||
});
|
||||
|
||||
// Goes to the previous track in the playlist
|
||||
ipcMain.on('player-previous', async () => {
|
||||
await mpv.prev();
|
||||
});
|
||||
|
||||
// Seeks forward or backward by the given amount of seconds
|
||||
ipcMain.on('player-seek', async (_event, time: number) => {
|
||||
await mpv.seek(time);
|
||||
});
|
||||
|
||||
// Seeks to the given time in seconds
|
||||
ipcMain.on('player-seek-to', async (_event, time: number) => {
|
||||
await mpv.goToPosition(time);
|
||||
});
|
||||
|
||||
// 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) => {
|
||||
if (data.queue.current) {
|
||||
await mpv.load(data.queue.current.streamUrl, 'replace');
|
||||
}
|
||||
|
||||
if (data.queue.next) {
|
||||
await mpv.load(data.queue.next.streamUrl, 'append');
|
||||
}
|
||||
});
|
||||
|
||||
// Replaces the queue in position 1 to the given data
|
||||
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
|
||||
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
|
||||
ipcMain.on('player-auto-next', async (_event, data: PlayerData) => {
|
||||
// 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) {
|
||||
await mpv.load(data.queue.next.streamUrl, 'append');
|
||||
}
|
||||
});
|
||||
|
||||
// Sets the volume to the given value (0-100)
|
||||
ipcMain.on('player-volume', async (_event, value: number) => {
|
||||
mpv.volume(value);
|
||||
});
|
||||
|
||||
// Toggles the mute status
|
||||
ipcMain.on('player-mute', async () => {
|
||||
mpv.mute();
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import Store from 'electron-store';
|
||||
|
||||
export const store = new Store();
|
||||
@@ -0,0 +1,3 @@
|
||||
import './core';
|
||||
|
||||
// require(`./${process.platform}`);
|
||||
@@ -0,0 +1,71 @@
|
||||
import { app, ipcMain } from 'electron';
|
||||
import './security-restrictions';
|
||||
import { restoreOrCreateWindow } from '/@/mainWindow';
|
||||
|
||||
ipcMain.on('app-restart', () => {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
/**
|
||||
* Prevent electron from running multiple instances.
|
||||
*/
|
||||
const isSingleInstance = app.requestSingleInstanceLock();
|
||||
if (!isSingleInstance) {
|
||||
app.quit();
|
||||
process.exit(0);
|
||||
}
|
||||
app.on('second-instance', restoreOrCreateWindow);
|
||||
|
||||
/**
|
||||
* Disable Hardware Acceleration to save more system resources.
|
||||
*/
|
||||
app.disableHardwareAcceleration();
|
||||
|
||||
/**
|
||||
* Shout down background process if all windows was closed
|
||||
*/
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://www.electronjs.org/docs/latest/api/app#event-activate-macos Event: 'activate'.
|
||||
*/
|
||||
app.on('activate', restoreOrCreateWindow);
|
||||
|
||||
/**
|
||||
* Create the application window when the background process is ready.
|
||||
*/
|
||||
app
|
||||
.whenReady()
|
||||
.then(restoreOrCreateWindow)
|
||||
.catch((e) => console.error('Failed create window:', e));
|
||||
|
||||
/**
|
||||
* Install Vue.js or any other extension in development mode only.
|
||||
* Note: You must install `electron-devtools-installer` manually
|
||||
*/
|
||||
// if (import.meta.env.DEV) {
|
||||
// app.whenReady()
|
||||
// .then(() => import('electron-devtools-installer'))
|
||||
// .then(({default: installExtension, VUEJS3_DEVTOOLS}) => installExtension(VUEJS3_DEVTOOLS, {
|
||||
// loadExtensionOptions: {
|
||||
// allowFileAccess: true,
|
||||
// },
|
||||
// }))
|
||||
// .catch(e => console.error('Failed install extension:', e));
|
||||
// }
|
||||
|
||||
/**
|
||||
* Check for new version of the application - production mode only.
|
||||
*/
|
||||
if (import.meta.env.PROD) {
|
||||
app
|
||||
.whenReady()
|
||||
.then(() => import('electron-updater'))
|
||||
.then(({ autoUpdater }) => autoUpdater.checkForUpdatesAndNotify())
|
||||
.catch((e) => console.error('Failed check updates:', e));
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { URL } from 'url';
|
||||
import { disableMediaKeys, enableMediaKeys } from './features/core/media-keys';
|
||||
import { store } from './features/core/settings';
|
||||
import electronLocalShortcut from 'electron-localshortcut';
|
||||
import './features';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
let browserWindow: BrowserWindow | null = null;
|
||||
|
||||
async function createWindow() {
|
||||
if (isDevelopment) {
|
||||
await installExtensions();
|
||||
}
|
||||
|
||||
browserWindow = new BrowserWindow({
|
||||
show: false, // Use the 'ready-to-show' event to show the instantiated BrowserWindow.
|
||||
frame: false,
|
||||
minWidth: 640,
|
||||
minHeight: 600,
|
||||
height: 900,
|
||||
width: 1440,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
sandbox: false, // Sandbox disabled because the demo of preload script depend on the Node.js api
|
||||
webviewTag: false, // The webview tag is not recommended. Consider alternatives like an iframe or Electron's BrowserView. @see https://www.electronjs.org/docs/latest/api/webview-tag#warning
|
||||
preload: join(app.getAppPath(), 'packages/preload/dist/index.cjs'),
|
||||
backgroundThrottling: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* If the 'show' property of the BrowserWindow's constructor is omitted from the initialization options,
|
||||
* it then defaults to 'true'. This can cause flickering as the window loads the html content,
|
||||
* and it also has show problematic behaviour with the closing of the window.
|
||||
* Use `show: false` and listen to the `ready-to-show` event to show the window.
|
||||
*
|
||||
* @see https://github.com/electron/electron/issues/25012 for the afford mentioned issue.
|
||||
*/
|
||||
browserWindow.on('ready-to-show', () => {
|
||||
browserWindow?.show();
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
browserWindow?.webContents.openDevTools();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('window-maximize', () => {
|
||||
browserWindow?.maximize();
|
||||
});
|
||||
|
||||
ipcMain.on('window-unmaximize', () => {
|
||||
browserWindow?.unmaximize();
|
||||
});
|
||||
|
||||
ipcMain.on('window-minimize', () => {
|
||||
browserWindow?.minimize();
|
||||
});
|
||||
|
||||
ipcMain.on('window-close', () => {
|
||||
browserWindow?.close();
|
||||
});
|
||||
|
||||
electronLocalShortcut.register(browserWindow, 'Ctrl+Shift+I', () => {
|
||||
browserWindow?.webContents.openDevTools();
|
||||
});
|
||||
|
||||
const globalMediaKeysEnabled = store.get('global_media_hotkeys') as boolean;
|
||||
|
||||
if (globalMediaKeysEnabled) {
|
||||
enableMediaKeys(browserWindow);
|
||||
}
|
||||
|
||||
ipcMain.on('global-media-keys-enable', () => {
|
||||
enableMediaKeys(browserWindow);
|
||||
});
|
||||
|
||||
ipcMain.on('global-media-keys-disable', () => {
|
||||
disableMediaKeys();
|
||||
});
|
||||
|
||||
/**
|
||||
* URL for main window.
|
||||
* Vite dev server for development.
|
||||
* `file://../renderer/index.html` for production and test.
|
||||
*/
|
||||
const pageUrl =
|
||||
import.meta.env.DEV && import.meta.env.VITE_DEV_SERVER_URL !== undefined
|
||||
? import.meta.env.VITE_DEV_SERVER_URL
|
||||
: new URL('../renderer/dist/index.html', 'file://' + __dirname).toString();
|
||||
|
||||
await browserWindow.loadURL(pageUrl);
|
||||
|
||||
return browserWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore an existing BrowserWindow or Create a new BrowserWindow.
|
||||
*/
|
||||
export async function restoreOrCreateWindow() {
|
||||
let window = BrowserWindow.getAllWindows().find((w) => !w.isDestroyed());
|
||||
|
||||
if (window === undefined) {
|
||||
window = await createWindow();
|
||||
}
|
||||
|
||||
if (window.isMinimized()) {
|
||||
window.restore();
|
||||
}
|
||||
|
||||
window.focus();
|
||||
}
|
||||
|
||||
ipcMain.on('app-restart', () => {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
export const getBrowserWindow = () => {
|
||||
return browserWindow;
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
import {app, shell} from 'electron';
|
||||
import {URL} from 'url';
|
||||
|
||||
type Permissions =
|
||||
| 'clipboard-read'
|
||||
| 'media'
|
||||
| 'display-capture'
|
||||
| 'mediaKeySystem'
|
||||
| 'geolocation'
|
||||
| 'notifications'
|
||||
| 'midi'
|
||||
| 'midiSysex'
|
||||
| 'pointerLock'
|
||||
| 'fullscreen'
|
||||
| 'openExternal'
|
||||
| 'unknown';
|
||||
|
||||
/**
|
||||
* A list of origins that you allow open INSIDE the application and permissions for them.
|
||||
*
|
||||
* In development mode you need allow open `VITE_DEV_SERVER_URL`.
|
||||
*/
|
||||
const ALLOWED_ORIGINS_AND_PERMISSIONS = new Map<string, Set<Permissions>>(
|
||||
import.meta.env.DEV && import.meta.env.VITE_DEV_SERVER_URL
|
||||
? [[new URL(import.meta.env.VITE_DEV_SERVER_URL).origin, new Set()]]
|
||||
: [],
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of origins that you allow open IN BROWSER.
|
||||
* Navigation to the origins below is only possible if the link opens in a new window.
|
||||
*
|
||||
* @example
|
||||
* <a
|
||||
* target="_blank"
|
||||
* href="https://github.com/"
|
||||
* >
|
||||
*/
|
||||
const ALLOWED_EXTERNAL_ORIGINS = new Set<`https://${string}`>(['https://github.com']);
|
||||
|
||||
app.on('web-contents-created', (_, contents) => {
|
||||
/**
|
||||
* Block navigation to origins not on the allowlist.
|
||||
*
|
||||
* Navigation exploits are quite common. If an attacker can convince the app to navigate away from its current page,
|
||||
* they can possibly force the app to open arbitrary web resources/websites on the web.
|
||||
*
|
||||
* @see https://www.electronjs.org/docs/latest/tutorial/security#13-disable-or-limit-navigation
|
||||
*/
|
||||
contents.on('will-navigate', (event, url) => {
|
||||
const {origin} = new URL(url);
|
||||
if (ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent navigation
|
||||
event.preventDefault();
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(`Blocked navigating to disallowed origin: ${origin}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Block requests for disallowed permissions.
|
||||
* By default, Electron will automatically approve all permission requests.
|
||||
*
|
||||
* @see https://www.electronjs.org/docs/latest/tutorial/security#5-handle-session-permission-requests-from-remote-content
|
||||
*/
|
||||
contents.session.setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
const {origin} = new URL(webContents.getURL());
|
||||
|
||||
const permissionGranted = !!ALLOWED_ORIGINS_AND_PERMISSIONS.get(origin)?.has(permission);
|
||||
callback(permissionGranted);
|
||||
|
||||
if (!permissionGranted && import.meta.env.DEV) {
|
||||
console.warn(`${origin} requested permission for '${permission}', but was rejected.`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Hyperlinks leading to allowed sites are opened in the default browser.
|
||||
*
|
||||
* The creation of new `webContents` is a common attack vector. Attackers attempt to convince the app to create new windows,
|
||||
* frames, or other renderer processes with more privileges than they had before; or with pages opened that they couldn't open before.
|
||||
* You should deny any unexpected window creation.
|
||||
*
|
||||
* @see https://www.electronjs.org/docs/latest/tutorial/security#14-disable-or-limit-creation-of-new-windows
|
||||
* @see https://www.electronjs.org/docs/latest/tutorial/security#15-do-not-use-openexternal-with-untrusted-content
|
||||
*/
|
||||
contents.setWindowOpenHandler(({url}) => {
|
||||
const {origin} = new URL(url);
|
||||
|
||||
// @ts-expect-error Type checking is performed in runtime.
|
||||
if (ALLOWED_EXTERNAL_ORIGINS.has(origin)) {
|
||||
// Open url in default browser.
|
||||
shell.openExternal(url).catch(console.error);
|
||||
} else if (import.meta.env.DEV) {
|
||||
console.warn(`Blocked the opening of a disallowed origin: ${origin}`);
|
||||
}
|
||||
|
||||
// Prevent creating a new window.
|
||||
return {action: 'deny'};
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify webview options before creation.
|
||||
*
|
||||
* Strip away preload scripts, disable Node.js integration, and ensure origins are on the allowlist.
|
||||
*
|
||||
* @see https://www.electronjs.org/docs/latest/tutorial/security#12-verify-webview-options-before-creation
|
||||
*/
|
||||
contents.on('will-attach-webview', (event, webPreferences, params) => {
|
||||
const {origin} = new URL(params.src);
|
||||
if (!ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(`A webview tried to attach ${params.src}, but was blocked.`);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Strip away preload scripts if unused or verify their location is legitimate.
|
||||
delete webPreferences.preload;
|
||||
// @ts-expect-error `preloadURL` exists. - @see https://www.electronjs.org/docs/latest/api/web-contents#event-will-attach-webview
|
||||
delete webPreferences.preloadURL;
|
||||
|
||||
// Disable Node.js integration
|
||||
webPreferences.nodeIntegration = false;
|
||||
|
||||
// Enable contextIsolation
|
||||
webPreferences.contextIsolation = true;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import type {MockedClass} from 'vitest';
|
||||
import {beforeEach, expect, test, vi} from 'vitest';
|
||||
import {restoreOrCreateWindow} from '../src/mainWindow';
|
||||
|
||||
import {BrowserWindow} from 'electron';
|
||||
|
||||
/**
|
||||
* Mock real electron BrowserWindow API
|
||||
*/
|
||||
vi.mock('electron', () => {
|
||||
// Use "as unknown as" because vi.fn() does not have static methods
|
||||
const bw = vi.fn() as unknown as MockedClass<typeof BrowserWindow>;
|
||||
bw.getAllWindows = vi.fn(() => bw.mock.instances);
|
||||
bw.prototype.loadURL = vi.fn((_: string, __?: Electron.LoadURLOptions) => Promise.resolve());
|
||||
// Use "any" because the on function is overloaded
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
bw.prototype.on = vi.fn<any>();
|
||||
bw.prototype.destroy = vi.fn();
|
||||
bw.prototype.isDestroyed = vi.fn();
|
||||
bw.prototype.isMinimized = vi.fn();
|
||||
bw.prototype.focus = vi.fn();
|
||||
bw.prototype.restore = vi.fn();
|
||||
|
||||
const app: Pick<Electron.App, 'getAppPath'> = {
|
||||
getAppPath(): string {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
return {BrowserWindow: bw, app};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('Should create a new window', async () => {
|
||||
const {mock} = vi.mocked(BrowserWindow);
|
||||
expect(mock.instances).toHaveLength(0);
|
||||
|
||||
await restoreOrCreateWindow();
|
||||
expect(mock.instances).toHaveLength(1);
|
||||
expect(mock.instances[0].loadURL).toHaveBeenCalledOnce();
|
||||
expect(mock.instances[0].loadURL).toHaveBeenCalledWith(expect.stringMatching(/index\.html$/));
|
||||
});
|
||||
|
||||
test('Should restore an existing window', async () => {
|
||||
const {mock} = vi.mocked(BrowserWindow);
|
||||
|
||||
// Create a window and minimize it.
|
||||
await restoreOrCreateWindow();
|
||||
expect(mock.instances).toHaveLength(1);
|
||||
const appWindow = vi.mocked(mock.instances[0]);
|
||||
appWindow.isMinimized.mockReturnValueOnce(true);
|
||||
|
||||
await restoreOrCreateWindow();
|
||||
expect(mock.instances).toHaveLength(1);
|
||||
expect(appWindow.restore).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
test('Should create a new window if the previous one was destroyed', async () => {
|
||||
const {mock} = vi.mocked(BrowserWindow);
|
||||
|
||||
// Create a window and destroy it.
|
||||
await restoreOrCreateWindow();
|
||||
expect(mock.instances).toHaveLength(1);
|
||||
const appWindow = vi.mocked(mock.instances[0]);
|
||||
appWindow.isDestroyed.mockReturnValueOnce(true);
|
||||
|
||||
await restoreOrCreateWindow();
|
||||
expect(mock.instances).toHaveLength(2);
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"sourceMap": false,
|
||||
"moduleResolution": "Node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"isolatedModules": true,
|
||||
"types": ["node"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"/@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "../../types/**/*.d.ts"],
|
||||
"exclude": ["**/*.spec.ts", "**/*.test.ts"]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import {node} from '../../.electron-vendors.cache.json';
|
||||
import {join} from 'node:path';
|
||||
import {injectAppVersion} from '../../version/inject-app-version-plugin.mjs';
|
||||
|
||||
const PACKAGE_ROOT = __dirname;
|
||||
const PROJECT_ROOT = join(PACKAGE_ROOT, '../..');
|
||||
|
||||
/**
|
||||
* @type {import('vite').UserConfig}
|
||||
* @see https://vitejs.dev/config/
|
||||
*/
|
||||
const config = {
|
||||
mode: process.env.MODE,
|
||||
root: PACKAGE_ROOT,
|
||||
envDir: PROJECT_ROOT,
|
||||
resolve: {
|
||||
alias: {
|
||||
'/@/': join(PACKAGE_ROOT, 'src') + '/',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
ssr: true,
|
||||
sourcemap: 'inline',
|
||||
target: `node${node}`,
|
||||
outDir: 'dist',
|
||||
assetsDir: '.',
|
||||
minify: process.env.MODE !== 'development',
|
||||
lib: {
|
||||
entry: 'src/index.ts',
|
||||
formats: ['cjs'],
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: '[name].cjs',
|
||||
},
|
||||
},
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: false,
|
||||
},
|
||||
plugins: [injectAppVersion(PROJECT_ROOT)],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
const exit = () => {
|
||||
ipcRenderer.send('window-close');
|
||||
};
|
||||
const maximize = () => {
|
||||
ipcRenderer.send('window-maximize');
|
||||
};
|
||||
const minimize = () => {
|
||||
ipcRenderer.send('window-minimize');
|
||||
};
|
||||
const unmaximize = () => {
|
||||
ipcRenderer.send('window-unmaximize');
|
||||
};
|
||||
|
||||
export const browser = {
|
||||
minimize,
|
||||
maximize,
|
||||
unmaximize,
|
||||
exit,
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* @module preload
|
||||
*/
|
||||
|
||||
export { sha256sum } from './node-crypto';
|
||||
export { versions } from './versions';
|
||||
export { localSettings } from './local-settings';
|
||||
export { browser } from './browser';
|
||||
export { mpvPlayer, mpvPlayerListener } from './mpv-player';
|
||||
export { ipc } from './ipc';
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
const removeAllListeners = (channel: string) => {
|
||||
ipcRenderer.removeAllListeners(channel);
|
||||
};
|
||||
|
||||
export const ipc = {
|
||||
removeAllListeners,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import Store from 'electron-store';
|
||||
import { app, ipcRenderer } from 'electron';
|
||||
|
||||
const store = new Store();
|
||||
|
||||
const set = (property: string, value: string | Record<string, unknown> | boolean | string[]) => {
|
||||
store.set(`${property}`, value);
|
||||
};
|
||||
|
||||
const get = (property: string) => {
|
||||
return store.get(`${property}`);
|
||||
};
|
||||
|
||||
const restart = () => {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
};
|
||||
|
||||
const enableMediaKeys = () => {
|
||||
ipcRenderer.send('global-media-keys-enable');
|
||||
};
|
||||
|
||||
const disableMediaKeys = () => {
|
||||
ipcRenderer.send('global-media-keys-disable');
|
||||
};
|
||||
|
||||
export const localSettings = {
|
||||
set,
|
||||
get,
|
||||
enableMediaKeys,
|
||||
disableMediaKeys,
|
||||
restart,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user