diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1076a1f0f..102369529 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -182,8 +182,8 @@ jobs: echo "${{ env.pythonLocation }}/bin" >> $GITHUB_PATH - shell: bash run: | - # disable electron-builder signing - #export CSC_IDENTITY_AUTO_DISCOVERY=false + # disable electron-builder signing and make dist + export CSC_IDENTITY_AUTO_DISCOVERY=false make dist env: BUILD: osx diff --git a/.gitignore b/.gitignore index e60a6aa95..a1d4a5e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ locales/ /profile_stats.prof /.vscode __pycache__ +flaskserverport.json +electron/yarn.lock diff --git a/Makefile b/Makefile index 64d6bed84..c4e214104 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,19 @@ +# used for distlocal +OS=$(shell uname) + dist: version locales inx + python bin/generate-flaskserverport-file bash bin/build-python bash bin/build-electron bash bin/build-distribution-archives distclean: - rm -rf build dist inx locales artifacts win mac *.spec *.tar.gz *.zip electron/node_modules electron/dist + rm -rf build dist inx locales artifacts win mac *.spec *.tar.gz *.zip electron/node_modules electron/dist electron/build/mac electron/build/mac-arm64 electron/build/win-ia32-unpacked electron/build/linux-unpacked electron/build/linux-arm64-unpacked electron/src/lib/flaskserverport.json + +distlocal: + @case ${OS} in "Darwin") export BUILD=osx ;; "Linux")export BUILD=linux ;; *) export BUILD=windows ;; esac; export VERSION=local-build; make distclean && make dist; +manual: + make inx && cd electron && yarn install && cd .. .PHONY: inx inx: version locales diff --git a/bin/build-distribution-archives b/bin/build-distribution-archives index a79ad6416..b44cad463 100644 --- a/bin/build-distribution-archives +++ b/bin/build-distribution-archives @@ -1,4 +1,4 @@ -VERSION="$(echo ${GITHUB_REF} | sed -e 's|refs/heads/||' -e 's|refs/tags/||' -e 's|/|-|g')" +VERSION="${VERSION:-$(echo ${GITHUB_REF} | sed -e 's|refs/heads/||' -e 's|refs/tags/||' -e 's|/|-|g')}" OS="${BUILD:-$(uname)}" ARCH="$(uname -m)" mkdir artifacts @@ -6,7 +6,7 @@ mkdir artifacts if [ "$BUILD" = "osx" ]; then cp -a icons locales print LICENSE VERSION images/examples palettes symbols fonts tiles dbus inx dist/inkstitch.app/Contents/Resources # adding version to Info.plist - sed -i '' 's/0.0.0/'${VERSION}'/' dist/inkstitch.app/Contents/Info.plist + plutil -replace CFBundleShortVersionString -string ${VERSION} dist/inkstitch.app/Contents/Info.plist rm -rf dist/inkstitch/ # Install location for pkgbuild PKG_INSTALL_PATH="/tmp/inkstitch/" diff --git a/bin/build-electron b/bin/build-electron index b0e935728..cdc98f9d3 100755 --- a/bin/build-electron +++ b/bin/build-electron @@ -2,15 +2,6 @@ set -e set -x -ARCH="$(uname -m)" -# Check for cpu arch to build mac and linux electron arch -if [[ "$ARCH" = "arm64" ]] || [[ "$ARCH" = "aarch64" ]]; then - echo "Found ARM" - sed -i'' -e 's/CPU_ARCH/'arm64'/' electron/package.json -else - echo "Found x64" - sed -i'' -e 's/CPU_ARCH/'x64'/' electron/package.json -fi if [ "$BUILD" = "windows" ]; then args="-w --ia32" diff --git a/bin/build-python b/bin/build-python index 92e1802ad..b8dd61ba9 100755 --- a/bin/build-python +++ b/bin/build-python @@ -2,26 +2,6 @@ set -e info_year=$( date "+%Y" ) -site_packages="$(python -c "import os; print(os.path.dirname(os.__file__) + '/site-packages')")" -arch=$(uname -m) -if [ "$BUILD" = "linux" ]; then - pyinstaller_args+="--add-binary /usr/lib/"$arch"-linux-gnu/gio/modules/libgiolibproxy.so:. " - pyinstaller_args+="--add-binary /usr/lib/"$arch"-linux-gnu/libproxy.so.1:. " - pyinstaller_args+="--add-binary /lib/"$arch"-linux-gnu/libnsl.so.1:. " - pyinstaller_args+="--add-binary /usr/lib/"$arch"-linux-gnu/libxcb.so.1:. " -fi - -# This one's tricky. ink/stitch doesn't actually _use_ gi.repository.Gtk, -# but it does use GTK (through wxPython). pyinstaller has some special -# logic to handle GTK apps that is engaged when you import -# gi.repository.Gtk that pulls in things like themes, icons, etc. Without -# that, the Params dialog is unthemed and barely usable. This hidden -# import option is actually the only reason we had to install python-gi -# above! -pyinstaller_args+="--hidden-import gi.repository.Gtk " - -# mac and windows build seem to miss wx import -pyinstaller_args+="--hidden-import wx " # We need to use the precompiled bootloader linked with graphical Mac OS X # libraries if we develop a GUI application for Mac: @@ -29,9 +9,6 @@ if [ "$BUILD" = "osx" -o "$BUILD" = "windows" ]; then pyinstaller_args+="--windowed " fi -# This lets pyinstaller see inkex.py, etc. -pyinstaller_args+="-p inkscape/share/extensions " - # output useful debugging info that helps us trace library dependency issues pyinstaller_args+="--log-level DEBUG " @@ -67,7 +44,7 @@ if [ "$BUILD" = "windows" ]; then pyinstaller_args+="--version-file installer_scripts/file_version_info.txt " python -m PyInstaller $pyinstaller_args inkstitch.py else - LD_LIBRARY_PATH="${site_packages}/wx" python -m PyInstaller $pyinstaller_args --strip inkstitch.py; + python -m PyInstaller $pyinstaller_args --strip inkstitch.py; fi # pyinstaller put a whole mess of libraries under dist/inkstitch. We'd like diff --git a/bin/build-windows-installer b/bin/build-windows-installer old mode 100644 new mode 100755 index a2d421ce5..90f72d711 --- a/bin/build-windows-installer +++ b/bin/build-windows-installer @@ -1,5 +1,5 @@ #!/bin/bash -VERSION="$(echo ${GITHUB_REF} | sed -e 's|refs/heads/||' -e 's|refs/tags/||' -e 's|/|-|g')" +VERSION="${VERSION:-$(echo ${GITHUB_REF} | sed -e 's|refs/heads/||' -e 's|refs/tags/||' -e 's|/|-|g')}" OS="${BUILD:-$(uname)}" # Create windows installer mkdir win diff --git a/bin/generate-flaskserverport-file b/bin/generate-flaskserverport-file new file mode 100755 index 000000000..d13239e9a --- /dev/null +++ b/bin/generate-flaskserverport-file @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import os +from json import dump +from os.path import dirname + +path = os.path.join(dirname(dirname(__file__)), 'electron', 'src', 'lib', 'flaskserverport.json') + +data = {"_comment1" : "port should not be declared when commiting"} +# write data to font.json into the same directory as the font file +with open(path, 'w', encoding="utf8") as output: + dump(data, output, indent=4, ensure_ascii=False) diff --git a/bin/notarize-app b/bin/notarize-app deleted file mode 100644 index 4a2bbc418..000000000 --- a/bin/notarize-app +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -u -# source of this code: https://github.com/rednoah/notarize-app -ASC_PROVIDER="$1" -ASC_USERNAME="$2" -ASC_PASSWORD="$3" - -BUNDLE_ID="$4" -BUNDLE_PKG="$5" - - -# create temporary files -NOTARIZE_APP_LOG=$(mktemp -t notarize-app) -NOTARIZE_INFO_LOG=$(mktemp -t notarize-info) - -# delete temporary files on exit -function finish { - rm "$NOTARIZE_APP_LOG" "$NOTARIZE_INFO_LOG" -} -trap finish EXIT - - -# submit app for notarization -if xcrun altool --notarize-app --primary-bundle-id "$BUNDLE_ID" --asc-provider "$ASC_PROVIDER" --username "$ASC_USERNAME" --password "$ASC_PASSWORD" -f "$BUNDLE_PKG" > "$NOTARIZE_APP_LOG" 2>&1; then - cat "$NOTARIZE_APP_LOG" - RequestUUID=$(awk -F ' = ' '/RequestUUID/ {print $2}' "$NOTARIZE_APP_LOG") - - # check status periodically - while sleep 60 && date; do - # check notarization status - if xcrun altool --notarization-info "$RequestUUID" --asc-provider "$ASC_PROVIDER" --username "$ASC_USERNAME" --password "$ASC_PASSWORD" > "$NOTARIZE_INFO_LOG" 2>&1; then - cat "$NOTARIZE_INFO_LOG" - - # once notarization is complete, run stapler and exit - if ! grep -q "Status: in progress" "$NOTARIZE_INFO_LOG"; then - xcrun stapler staple "$BUNDLE_PKG" - exit $? - fi - else - cat "$NOTARIZE_INFO_LOG" 1>&2 - exit 1 - fi - done -else - cat "$NOTARIZE_APP_LOG" 1>&2 - exit 1 -fi diff --git a/electron/.babelrc b/electron/.babelrc deleted file mode 100644 index b10dbaf10..000000000 --- a/electron/.babelrc +++ /dev/null @@ -1,30 +0,0 @@ -{ - "comments": false, - "env": { - "main": { - "presets": [ - ["env", { - "targets": { "node": 7 } - }], - "stage-0" - ] - }, - "renderer": { - "presets": [ - ["env", { - "modules": false - }], - "stage-0" - ] - }, - "web": { - "presets": [ - ["env", { - "modules": false - }], - "stage-0" - ] - } - }, - "plugins": ["transform-runtime"] -} diff --git a/electron/.browserslistrc b/electron/.browserslistrc new file mode 100644 index 000000000..b62a035bf --- /dev/null +++ b/electron/.browserslistrc @@ -0,0 +1,4 @@ +# https://github.com/browserslist/browserslist#queries +> 1% +last 2 versions +not dead diff --git a/electron/.compilerc b/electron/.compilerc deleted file mode 100644 index 7bfc1b6c3..000000000 --- a/electron/.compilerc +++ /dev/null @@ -1,42 +0,0 @@ -{ - "env": { - "development": { - "application/javascript": { - "presets": [ - [ - "env", - { - "targets": { - "electron": "4.0" - } - } - ], - "react" - ], - "plugins": [ - "transform-async-to-generator" - ], - "sourceMaps": "inline" - } - }, - "production": { - "application/javascript": { - "presets": [ - [ - "env", - { - "targets": { - "electron": "4.0" - } - } - ], - "react" - ], - "plugins": [ - "transform-async-to-generator" - ], - "sourceMaps": "none" - } - } - } -} diff --git a/electron/.electron-vue/build.js b/electron/.electron-vue/build.js deleted file mode 100644 index 2455fbcb2..000000000 --- a/electron/.electron-vue/build.js +++ /dev/null @@ -1,132 +0,0 @@ -'use strict' - -process.env.NODE_ENV = 'production' - -const { say } = require('cfonts') -const chalk = require('chalk') -const del = require('del') -const { spawn } = require('child_process') -const webpack = require('webpack') -const Multispinner = require('multispinner') - - -const mainConfig = require('./webpack.main.config') -const rendererConfig = require('./webpack.renderer.config') -const webConfig = require('./webpack.web.config') - -const doneLog = chalk.bgGreen.white(' DONE ') + ' ' -const errorLog = chalk.bgRed.white(' ERROR ') + ' ' -const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' -const isCI = process.env.CI || false - -if (process.env.BUILD_TARGET === 'clean') clean() -else if (process.env.BUILD_TARGET === 'web') web() -else build() - -function clean () { - del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) - console.log(`\n${doneLog}\n`) - process.exit() -} - -function build () { - greeting() - - del.sync(['dist/electron/*', '!.gitkeep']) - - const tasks = ['main', 'renderer'] - const m = new Multispinner(tasks, { - preText: 'building', - postText: 'process' - }) - - let results = '' - - m.on('success', () => { - process.stdout.write('\x1B[2J\x1B[0f') - console.log(`\n\n${results}`) - console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) - process.exit() - }) - - pack(mainConfig).then(result => { - results += result + '\n\n' - m.success('main') - }).catch(err => { - m.error('main') - console.log(`\n ${errorLog}failed to build main process`) - console.error(`\n${err}\n`) - process.exit(1) - }) - - pack(rendererConfig).then(result => { - results += result + '\n\n' - m.success('renderer') - }).catch(err => { - m.error('renderer') - console.log(`\n ${errorLog}failed to build renderer process`) - console.error(`\n${err}\n`) - process.exit(1) - }) -} - -function pack (config) { - return new Promise((resolve, reject) => { - config.mode = 'production' - webpack(config, (err, stats) => { - if (err) reject(err.stack || err) - else if (stats.hasErrors()) { - let err = '' - - stats.toString({ - chunks: false, - colors: true - }) - .split(/\r?\n/) - .forEach(line => { - err += ` ${line}\n` - }) - - reject(err) - } else { - resolve(stats.toString({ - chunks: false, - colors: true - })) - } - }) - }) -} - -function web () { - del.sync(['dist/web/*', '!.gitkeep']) - webConfig.mode = 'production' - webpack(webConfig, (err, stats) => { - if (err || stats.hasErrors()) console.log(err) - - console.log(stats.toString({ - chunks: false, - colors: true - })) - - process.exit() - }) -} - -function greeting () { - const cols = process.stdout.columns - let text = '' - - if (cols > 85) text = 'lets-build' - else if (cols > 60) text = 'lets-|build' - else text = false - - if (text && !isCI) { - say(text, { - colors: ['yellow'], - font: 'simple3d', - space: false - }) - } else console.log(chalk.yellow.bold('\n lets-build')) - console.log() -} \ No newline at end of file diff --git a/electron/.electron-vue/dev-client.js b/electron/.electron-vue/dev-client.js deleted file mode 100644 index 2913ea4b0..000000000 --- a/electron/.electron-vue/dev-client.js +++ /dev/null @@ -1,40 +0,0 @@ -const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') - -hotClient.subscribe(event => { - /** - * Reload browser when HTMLWebpackPlugin emits a new index.html - * - * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. - * https://github.com/SimulatedGREG/electron-vue/issues/437 - * https://github.com/jantimon/html-webpack-plugin/issues/680 - */ - // if (event.action === 'reload') { - // window.location.reload() - // } - - /** - * Notify `mainWindow` when `main` process is compiling, - * giving notice for an expected reload of the `electron` process - */ - if (event.action === 'compiling') { - document.body.innerHTML += ` - - -
- Compiling Main Process... -
- ` - } -}) diff --git a/electron/.electron-vue/dev-runner.js b/electron/.electron-vue/dev-runner.js deleted file mode 100644 index 181df4111..000000000 --- a/electron/.electron-vue/dev-runner.js +++ /dev/null @@ -1,192 +0,0 @@ -'use strict' - -const chalk = require('chalk') -const electron = require('electron') -const path = require('path') -const { say } = require('cfonts') -const { spawn } = require('child_process') -const webpack = require('webpack') -const WebpackDevServer = require('webpack-dev-server') -const webpackHotMiddleware = require('webpack-hot-middleware') - -const mainConfig = require('./webpack.main.config') -const rendererConfig = require('./webpack.renderer.config') - -let electronProcess = null -let manualRestart = false -let hotMiddleware - -function logStats (proc, data) { - let log = '' - - log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) - log += '\n\n' - - if (typeof data === 'object') { - data.toString({ - colors: true, - chunks: false - }).split(/\r?\n/).forEach(line => { - log += ' ' + line + '\n' - }) - } else { - log += ` ${data}\n` - } - - log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' - - console.log(log) -} - -function startRenderer () { - return new Promise((resolve, reject) => { - rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) - rendererConfig.mode = 'development' - const compiler = webpack(rendererConfig) - hotMiddleware = webpackHotMiddleware(compiler, { - log: false, - heartbeat: 2500 - }) - - compiler.hooks.compilation.tap('compilation', compilation => { - compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => { - hotMiddleware.publish({ action: 'reload' }) - cb() - }) - }) - - compiler.hooks.done.tap('done', stats => { - logStats('Renderer', stats) - }) - - const server = new WebpackDevServer( - compiler, - { - contentBase: path.join(__dirname, '../'), - quiet: true, - before (app, ctx) { - app.use(hotMiddleware) - ctx.middleware.waitUntilValid(() => { - resolve() - }) - } - } - ) - - server.listen(9080) - }) -} - -function startMain () { - return new Promise((resolve, reject) => { - mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) - mainConfig.mode = 'development' - const compiler = webpack(mainConfig) - - compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => { - logStats('Main', chalk.white.bold('compiling...')) - hotMiddleware.publish({ action: 'compiling' }) - done() - }) - - compiler.watch({}, (err, stats) => { - if (err) { - console.log(err) - return - } - - logStats('Main', stats) - - if (electronProcess && electronProcess.kill) { - manualRestart = true - process.kill(electronProcess.pid) - electronProcess = null - startElectron() - - setTimeout(() => { - manualRestart = false - }, 5000) - } - - resolve() - }) - }) -} - -function startElectron () { - process.argv.shift(); - process.argv.shift(); - var args = [ - '--inspect=5858', - path.join(__dirname, '../dist/electron/main.js') - ].concat(process.argv) - - // detect yarn or npm and process commandline args accordingly - if (process.env.npm_execpath.endsWith('yarn.js')) { - args = args.concat(process.argv.slice(3)) - } else if (process.env.npm_execpath.endsWith('npm-cli.js')) { - args = args.concat(process.argv.slice(2)) - } - - electronProcess = spawn(electron, args) - - electronProcess.stdout.on('data', data => { - electronLog(data, 'blue') - }) - electronProcess.stderr.on('data', data => { - electronLog(data, 'red') - }) - - electronProcess.on('close', () => { - if (!manualRestart) process.exit() - }) -} - -function electronLog (data, color) { - let log = '' - data = data.toString().split(/\r?\n/) - data.forEach(line => { - log += ` ${line}\n` - }) - if (/[0-9A-z]+/.test(log)) { - console.log( - chalk[color].bold('┏ Electron -------------------') + - '\n\n' + - log + - chalk[color].bold('┗ ----------------------------') + - '\n' - ) - } -} - -function greeting () { - const cols = process.stdout.columns - let text = '' - - if (cols > 104) text = 'electron-vue' - else if (cols > 76) text = 'electron-|vue' - else text = false - - if (text) { - say(text, { - colors: ['yellow'], - font: 'simple3d', - space: false - }) - } else console.log(chalk.yellow.bold('\n electron-vue')) - console.log(chalk.blue(' getting ready...') + '\n') -} - -function init () { - greeting() - - Promise.all([startRenderer(), startMain()]) - .then(() => { - startElectron() - }) - .catch(err => { - console.error(err) - }) -} - -init() diff --git a/electron/.electron-vue/webpack.main.config.js b/electron/.electron-vue/webpack.main.config.js deleted file mode 100644 index cfdbdf22c..000000000 --- a/electron/.electron-vue/webpack.main.config.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict' - -process.env.BABEL_ENV = 'main' - -const path = require('path') -const { dependencies } = require('../package.json') -const webpack = require('webpack') - -const BabiliWebpackPlugin = require('babili-webpack-plugin') - -let mainConfig = { - entry: { - main: path.join(__dirname, '../src/main/index.js') - }, - externals: [ - ...Object.keys(dependencies || {}) - ], - module: { - rules: [ - { - test: /\.js$/, - use: 'babel-loader', - exclude: /node_modules/ - }, - { - test: /\.node$/, - use: 'node-loader' - } - ] - }, - node: { - __dirname: process.env.NODE_ENV !== 'production', - __filename: process.env.NODE_ENV !== 'production' - }, - output: { - filename: '[name].js', - libraryTarget: 'commonjs2', - path: path.join(__dirname, '../dist/electron') - }, - plugins: [ - new webpack.NoEmitOnErrorsPlugin() - ], - resolve: { - extensions: ['.js', '.json', '.node'] - }, - target: 'electron-main' -} - -/** - * Adjust mainConfig for development settings - */ -if (process.env.NODE_ENV !== 'production') { - mainConfig.plugins.push( - new webpack.DefinePlugin({ - '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` - }) - ) -} - -/** - * Adjust mainConfig for production settings - */ -if (process.env.NODE_ENV === 'production') { - mainConfig.plugins.push( - new BabiliWebpackPlugin(), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': '"production"' - }) - ) -} - -module.exports = mainConfig diff --git a/electron/.electron-vue/webpack.renderer.config.js b/electron/.electron-vue/webpack.renderer.config.js deleted file mode 100644 index 8a848141a..000000000 --- a/electron/.electron-vue/webpack.renderer.config.js +++ /dev/null @@ -1,202 +0,0 @@ -'use strict' - -process.env.BABEL_ENV = 'renderer' - -const path = require('path') -const { dependencies } = require('../package.json') -const webpack = require('webpack') - -const BabiliWebpackPlugin = require('babili-webpack-plugin') -const CopyWebpackPlugin = require('copy-webpack-plugin') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const { VueLoaderPlugin } = require('vue-loader') - -/** - * List of node_modules to include in webpack bundle - * - * Required for specific packages like Vue UI libraries - * that provide pure *.vue files that need compiling - * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals - */ -// 'vue-slider-component' needed here due to this issue: -// https://github.com/SimulatedGREG/electron-vue/issues/725 -let whiteListedModules = ['vue', 'vue-slider-component', 'vuetify'] - -let rendererConfig = { - devtool: '#cheap-module-eval-source-map', - entry: { - renderer: path.join(__dirname, '../src/renderer/main.js') - }, - externals: [ - ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) - ], - module: { - rules: [ - { - test: /\.less$/, - use: ['vue-style-loader', 'css-loader', 'less-loader'] - }, - { - test: /\.css$/, - use: ['vue-style-loader', 'css-loader'] - }, - { - test: /\.html$/, - use: 'vue-html-loader' - }, - { - test: /\.js$/, - use: 'babel-loader', - exclude: /node_modules/ - }, - { - test: /\.node$/, - use: 'node-loader' - }, - { - test: /\.vue$/, - use: { - loader: 'vue-loader', - options: { - extractCSS: process.env.NODE_ENV === 'production', - loaders: { - sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', - scss: 'vue-style-loader!css-loader!sass-loader', - less: 'vue-style-loader!css-loader!less-loader' - } - } - } - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - use: { - loader: 'url-loader', - query: { - limit: 10000, - name: 'imgs/[name]--[folder].[ext]' - } - } - }, - { - test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: 'media/[name]--[folder].[ext]' - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - use: { - loader: 'url-loader', - query: { - limit: 10000, - name: 'fonts/[name]--[folder].[ext]' - } - } - }, - { - test: /\.s(c|a)ss$/, - use: [ - 'vue-style-loader', - 'css-loader', - { - loader: 'sass-loader', - // Requires sass-loader@^8.0.0 - options: { - implementation: require('sass'), - sassOptions: { - indentedSyntax: true // optional - }, - }, - }, - ], - } - ] - }, - node: { - __dirname: process.env.NODE_ENV !== 'production', - __filename: process.env.NODE_ENV !== 'production' - }, - plugins: [ - new VueLoaderPlugin(), - new MiniCssExtractPlugin({filename: 'styles.css'}), - new HtmlWebpackPlugin({ - filename: 'index.html', - template: path.resolve(__dirname, '../src/index.ejs'), - templateParameters(compilation, assets, options) { - return { - compilation: compilation, - webpack: compilation.getStats().toJson(), - webpackConfig: compilation.options, - htmlWebpackPlugin: { - files: assets, - options: options - }, - process - } - }, - minify: { - collapseWhitespace: true, - removeAttributeQuotes: true, - removeComments: true - }, - nodeModules: process.env.NODE_ENV !== 'production' - ? path.resolve(__dirname, '../node_modules') - : false - }), - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ], - output: { - filename: '[name].js', - libraryTarget: 'commonjs2', - path: path.join(__dirname, '../dist/electron') - }, - resolve: { - alias: { - '@': path.join(__dirname, '../src/renderer'), - 'vue$': 'vue/dist/vue.esm.js' - }, - extensions: ['.js', '.vue', '.json', '.css', '.node'] - }, - target: 'electron-renderer' -} - -/** - * Adjust rendererConfig for development settings - */ -if (process.env.NODE_ENV !== 'production') { - rendererConfig.plugins.push( - new webpack.DefinePlugin({ - '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` - }) - ) -} - -/** - * Adjust rendererConfig for production settings - */ -if (process.env.NODE_ENV === 'production') { - rendererConfig.devtool = '' - - rendererConfig.plugins.push( - new BabiliWebpackPlugin(), - new CopyWebpackPlugin([ - { - from: path.join(__dirname, '../static'), - to: path.join(__dirname, '../dist/electron/static'), - ignore: ['.*'] - } - ]), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': '"production"' - }), - new webpack.LoaderOptionsPlugin({ - minimize: true - }) - ) -} - -module.exports = rendererConfig diff --git a/electron/.electron-vue/webpack.web.config.js b/electron/.electron-vue/webpack.web.config.js deleted file mode 100644 index c19e42c5a..000000000 --- a/electron/.electron-vue/webpack.web.config.js +++ /dev/null @@ -1,161 +0,0 @@ -'use strict' - -process.env.BABEL_ENV = 'web' - -const path = require('path') -const webpack = require('webpack') - -const BabiliWebpackPlugin = require('babili-webpack-plugin') -const CopyWebpackPlugin = require('copy-webpack-plugin') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const { VueLoaderPlugin } = require('vue-loader') - -let webConfig = { - devtool: '#cheap-module-eval-source-map', - entry: { - web: path.join(__dirname, '../src/renderer/main.js') - }, - module: { - rules: [ - { - test: /\.less$/, - use: ['vue-style-loader', 'css-loader', 'less-loader'] - }, - { - test: /\.css$/, - use: ['vue-style-loader', 'css-loader'] - }, - { - test: /\.html$/, - use: 'vue-html-loader' - }, - { - test: /\.js$/, - use: 'babel-loader', - include: [ path.resolve(__dirname, '../src/renderer') ], - exclude: /node_modules/ - }, - { - test: /\.vue$/, - use: { - loader: 'vue-loader', - options: { - extractCSS: true, - loaders: { - sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', - scss: 'vue-style-loader!css-loader!sass-loader', - less: 'vue-style-loader!css-loader!less-loader' - } - } - } - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - use: { - loader: 'url-loader', - query: { - limit: 10000, - name: 'imgs/[name].[ext]' - } - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - use: { - loader: 'url-loader', - query: { - limit: 10000, - name: 'fonts/[name].[ext]' - } - } - }, - { - test: /\.s(c|a)ss$/, - use: [ - 'vue-style-loader', - 'css-loader', - { - loader: 'sass-loader', - // Requires sass-loader@^8.0.0 - options: { - implementation: require('sass'), - sassOptions: { - indentedSyntax: true // optional - }, - }, - }, - ], - }, - ] - }, - plugins: [ - new VueLoaderPlugin(), - new MiniCssExtractPlugin({filename: 'styles.css'}), - new HtmlWebpackPlugin({ - filename: 'index.html', - template: path.resolve(__dirname, '../src/index.ejs'), - templateParameters(compilation, assets, options) { - return { - compilation: compilation, - webpack: compilation.getStats().toJson(), - webpackConfig: compilation.options, - htmlWebpackPlugin: { - files: assets, - options: options - }, - process - } - }, - minify: { - collapseWhitespace: true, - removeAttributeQuotes: true, - removeComments: true - }, - nodeModules: false - }), - new webpack.DefinePlugin({ - 'process.env.IS_WEB': 'true' - }), - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ], - output: { - filename: '[name].js', - path: path.join(__dirname, '../dist/web') - }, - resolve: { - alias: { - '@': path.join(__dirname, '../src/renderer'), - 'vue$': 'vue/dist/vue.esm.js' - }, - extensions: ['.js', '.vue', '.json', '.css'] - }, - target: 'web' -} - -/** - * Adjust webConfig for production settings - */ -if (process.env.NODE_ENV === 'production') { - webConfig.devtool = '' - - webConfig.plugins.push( - new BabiliWebpackPlugin(), - new CopyWebpackPlugin([ - { - from: path.join(__dirname, '../static'), - to: path.join(__dirname, '../dist/web/static'), - ignore: ['.*'] - } - ]), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': '"production"' - }), - new webpack.LoaderOptionsPlugin({ - minimize: true - }) - ) -} - -module.exports = webConfig diff --git a/electron/.eslintignore b/electron/.eslintignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/electron/.eslintrc b/electron/.eslintrc deleted file mode 100644 index 64f12522e..000000000 --- a/electron/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "eslint-config-airbnb", - "rules": { - "import/extensions": 0, - "import/no-extraneous-dependencies": 0, - "import/no-unresolved": [2, { "ignore": ["electron"] }], - "linebreak-style": 0 - } -} diff --git a/electron/.eslintrc.js b/electron/.eslintrc.js deleted file mode 100644 index 90cf05bbf..000000000 --- a/electron/.eslintrc.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - root: true, - parser: 'babel-eslint', - parserOptions: { - sourceType: 'module' - }, - env: { - browser: true, - node: true - }, - extends: 'standard', - globals: { - __static: true - }, - plugins: [ - 'html' - ], - 'rules': { - // allow paren-less arrow functions - 'arrow-parens': 0, - // allow async-await - 'generator-star-spacing': 0, - // allow debugger during development - 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 - } -} diff --git a/electron/.gitattributes b/electron/.gitattributes new file mode 100644 index 000000000..6313b56c5 --- /dev/null +++ b/electron/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/electron/babel.config.js b/electron/babel.config.js new file mode 100644 index 000000000..ff2f95bea --- /dev/null +++ b/electron/babel.config.js @@ -0,0 +1,16 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + useBuiltIns: 'usage', // adds specific imports for polyfills when they are used in each file. + modules: false, // preserve ES modules. + corejs: { version: 3, proposals: true }, // enable polyfilling of every proposal supported by core-js. + }, + ], + ], + plugins: [ + '@babel/plugin-transform-runtime', // enables the re-use of Babel's injected helper code to save on codesize. + ], + exclude: [/core-js/], +} diff --git a/electron/package.json b/electron/package.json index 7a0b318b9..dadf713d6 100644 --- a/electron/package.json +++ b/electron/package.json @@ -4,10 +4,12 @@ "version": "11.99.11", "description": "Ink/Stitch GUI", "main": "./dist/electron/main.js", + "private": true, "scripts": { - "pack": "electron-builder --dir", - "dist": "node .electron-vue/build.js && electron-builder", - "dev": "node --openssl-legacy-provider .electron-vue/dev-runner.js" + "dev": "node service/commands/dev.js", + "just-build": "node service/commands/build.js && node service/commands/build-main.js", + "dist": "yarn just-build && electron-builder", + "print": "yarn just-build && yarn dev" }, "build": { "productName": "inkstitch-gui", @@ -22,10 +24,7 @@ "icon": "build/icons", "target": [ { - "target": "dir", - "arch": [ - "CPU_ARCH" - ] + "target": "dir" } ] }, @@ -37,10 +36,7 @@ "icon": "build/icons/mac/inkstitch.icns", "target": [ { - "target": "dir", - "arch": [ - "CPU_ARCH" - ] + "target": "dir" } ], "hardenedRuntime": true, @@ -54,78 +50,70 @@ "author": "lex", "license": "GPL-3.0-or-later", "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.22", - "@fortawesome/free-solid-svg-icons": "^5.10.2", - "@fortawesome/vue-fontawesome": "^0.1.6", - "@mdi/font": "^5.5.55", - "@svgdotjs/svg.filter.js": "^3.0.7", - "@svgdotjs/svg.js": "^3.0.16", - "@svgdotjs/svg.panzoom.js": "https://github.com/inkstitch/svg.panzoom.js", - "axios": "^0.19.0", - "electron-compile": "^6.4.4", + "@babel/plugin-transform-runtime": "^7.21.0", + "@babel/runtime": "^7.17.9", + "@fortawesome/fontawesome-svg-core": "^6.1.1", + "@fortawesome/free-solid-svg-icons": "^6.1.1", + "@fortawesome/vue-fontawesome": "^3.0.0-5", + "@svgdotjs/svg.filter.js": "^3.0.8", + "@svgdotjs/svg.js": "^3.1.2", + "@svgdotjs/svg.panzoom.js": "^2.1.2", + "axios": "^0.27.2", + "core-js": "^3.22.2", "lodash.throttle": "^4.1.1", - "mousetrap": "^1.6.3", - "query-string": "^6.8.2", - "svgpath": "^2.2.3", - "tmp": "0.1.0", - "vue": "^2.5.16", - "vue-gettext": "^2.1.5", - "vue-loading-overlay": "^3.2.0", - "vue-router": "^3.0.1", - "vue-slider-component": "^3.0.38", - "vue2-transitions": "^0.3.0", - "vuetify": "^2.3.9" + "node-polyfill-webpack-plugin": "^2.0.1", + "query-string": "^7.1.1", + "svgpath": "^2.5.0", + "vue": "^3.2.33", + "vue-loading-overlay": "^5.0", + "vue-mousetrap": "^1.0.5", + "vue-router": "4", + "vue-slider-component": "^4.1.0-beta.0", + "vue3-gettext": "^2.2.3", + "vue3-transitions": "^1.0.0", + "vuetify": "^3.1.7", + "webpack-plugin-vuetify": "^2.0.1" }, "devDependencies": { - "ajv": "^6.5.0", - "babel-core": "^6.26.3", - "babel-eslint": "^8.2.3", - "babel-loader": "^7.1.4", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-env": "^1.7.0", - "babel-preset-stage-0": "^6.24.1", - "babel-register": "^6.26.0", - "babili-webpack-plugin": "^0.1.2", - "cfonts": "^2.1.2", - "chalk": "^2.4.1", - "copy-webpack-plugin": "^4.5.1", - "cross-env": "^5.1.6", - "css-loader": "^0.28.11", + "@babel/core": "^7.17.9", + "@babel/preset-env": "^7.16.11", + "@types/webpack-env": "^1.16.4", + "@vue/compiler-sfc": "^3.2.33", + "autoprefixer": "^10.4.5", + "babel-loader": "^8.2.5", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "chalk": "^4.1.2", + "copy-webpack-plugin": "^10.2.4", + "css-loader": "^6.7.1", "deepmerge": "^4.2.2", - "del": "^3.0.0", - "devtron": "^1.4.0", - "easygettext": "^2.7.0", - "electron": "4.1.3", + "dotenv": "^16.0.0", + "dotenv-expand": "^8.0.3", + "electron": "14.2.9", "electron-builder": "^23.0.3", - "electron-debug": "^1.5.0", - "electron-devtools-installer": "^2.2.4", - "electron-prebuilt-compile": "4.0.0", - "eslint": "^4.19.1", - "eslint-config-standard": "^11.0.0", - "eslint-friendly-formatter": "^4.0.1", - "eslint-loader": "^2.0.0", - "eslint-plugin-html": "^4.0.3", - "eslint-plugin-import": "^2.12.0", - "eslint-plugin-node": "^6.0.1", - "eslint-plugin-promise": "^3.8.0", - "eslint-plugin-standard": "^3.1.0", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "0.4.0", - "multispinner": "^0.2.1", - "node-loader": "^0.6.0", - "sass": "^1.26.10", - "sass-loader": "^9.0.3", - "style-loader": "^0.21.0", - "url-loader": "^1.0.1", - "vue-html-loader": "^1.2.4", - "vue-loader": "^15.2.4", - "vue-style-loader": "^4.1.0", - "vue-template-compiler": "^2.5.16", - "webpack": "^4.15.1", - "webpack-cli": "^3.0.8", - "webpack-dev-server": "^3.1.4", - "webpack-hot-middleware": "^2.22.2", - "webpack-merge": "^4.1.3" + "html-webpack-plugin": "^5.5.0", + "mini-css-extract-plugin": "^2.6.0", + "ora": "^5.4.1", + "postcss": "^8.4.12", + "postcss-html": "^1.4.1", + "postcss-loader": "^6.2.1", + "rimraf": "^3.0.2", + "sass": "~1.32", + "sass-loader": "^13.0.0", + "strip-ansi": "^6.0.0", + "style-loader": "^3.3.1", + "terser-webpack-plugin": "^5.3.1", + "thread-loader": "^3.0.4", + "ts-loader": "^9.2.8", + "typescript": "^4.6.3", + "url-loader": "^4.1.1", + "vue-loader": "^17.0.0", + "vue-style-loader": "^4.1.3", + "wait-on": "^6.0.1", + "webpack": "^5.72.0", + "webpack-dev-server": "^4.8.1", + "webpack-merge": "^5.8.0" + }, + "engines": { + "node": ">=12.13.0" } } diff --git a/electron/service/commands/build-main.js b/electron/service/commands/build-main.js new file mode 100644 index 000000000..1b28ef91b --- /dev/null +++ b/electron/service/commands/build-main.js @@ -0,0 +1,41 @@ +'use strict' + +const loadEnv = require('../utils/loadEnv') +loadEnv() +loadEnv('production') + +const rm = require('rimraf') +const webpack = require('webpack') + +const { error, done } = require('../utils/logger') +const { logWithSpinner, stopSpinner } = require('../utils/spinner') +const paths = require('../utils/paths') +// after renderer is built, main is next to build +const webpackConfig = require('../config/main') +const config = require('../project.config') + +logWithSpinner('Building for production...') +// removed rm function to prevent the deletion of renderer +webpack(webpackConfig, (err, stats) => { + stopSpinner(false) + + if (err) throw err + + process.stdout.write( + stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false, + }) + '\n\n' + ) + + if (stats.hasErrors()) { + error('Build failed with errors.\n') + process.exit(1) + } + + done('Build complete.\n') +}) + diff --git a/electron/service/commands/build.js b/electron/service/commands/build.js new file mode 100644 index 000000000..097f70135 --- /dev/null +++ b/electron/service/commands/build.js @@ -0,0 +1,44 @@ +'use strict' + +const loadEnv = require('../utils/loadEnv') +loadEnv() +loadEnv('production') + +const rm = require('rimraf') +const webpack = require('webpack') + +const { error, done } = require('../utils/logger') +const { logWithSpinner, stopSpinner } = require('../utils/spinner') +const paths = require('../utils/paths') +// build renderer first +const webpackConfig = require('../config/renderer') +const config = require('../project.config') + +logWithSpinner('Building for production...') + +rm(paths.resolve(config.outputDir), (err) => { + if (err) throw err + + webpack(webpackConfig, (err, stats) => { + stopSpinner(false) + + if (err) throw err + + process.stdout.write( + stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false, + }) + '\n\n' + ) + + if (stats.hasErrors()) { + error('Build failed with errors.\n') + process.exit(1) + } + + done('Build complete.\n') + }) +}) diff --git a/electron/service/commands/dev.js b/electron/service/commands/dev.js new file mode 100644 index 000000000..bea420218 --- /dev/null +++ b/electron/service/commands/dev.js @@ -0,0 +1,102 @@ +'use strict' + +const loadEnv = require('../utils/loadEnv') +loadEnv() +loadEnv('development') +const chalk = require('chalk') +const webpack = require('webpack') +const WebpackDevServer = require('webpack-dev-server') +const { info } = require('../utils/logger') +const getLocalIP = require('../utils/getLocalIP') +const devWebpackConfig = require('../config/dev') +const devServerOptions = devWebpackConfig.devServer +const { spawn } = require('node:child_process') +const electron = require('electron') +const path = require('path') +const url = require('url') +const fs = require('fs'); + +let electronProcess = null +let manualRestart = false +// disable warnings in browser console +process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'false' + +const protocol = devServerOptions.https ? 'https' : 'http' +const host = devServerOptions.host || '0.0.0.0' +const port = devServerOptions.port || 8080 + +// older code that sets the url for the path I would assume +var parseArg = process.argv[2] || "" +var yarnArg = url.parse(parseArg) + +function resetPort() { + let resetData = { "_comment1": "port should not be declared when commiting" } + fs.writeFileSync(path.join(__dirname, "../../src/lib/flaskserverport.json"), JSON.stringify(resetData), 'utf8') + console.log("Resetting the flaskport") +} + +function startElectron(webpackport) { +var wbport = webpackport + // this sends url to proper position + process.argv.shift() + process.argv.shift() + // get URL from PrintPDF + // checks if url is http + if (yarnArg.protocol) { + var args = [ + '--inspect=5858', + path.join(__dirname, '../../dist/electron/main.js') + ].concat(process.argv) + } else { + var args = [ + '--inspect=5858', + `http://0.0.0.0:${wbport}/#${process.argv}` + ].concat(process.argv) + } + // detect yarn or npm and process commandline args accordingly + if (process.env.npm_execpath.endsWith('yarn.js')) { + args = args.concat(process.argv.slice(3)) + } else if (process.env.npm_execpath.endsWith('npm-cli.js')) { + args = args.concat(process.argv.slice(2)) + } + electronProcess = spawn(electron, args) + electronProcess.on('close', () => { + if (!manualRestart) { + process.exit() + } else { + process.kill(electronProcess.pid) + } + resetPort() + }) + electronProcess.on('exit', () => { + resetPort() + }) +} + +info('Starting development server...') +const compiler = webpack(devWebpackConfig) +const server = new WebpackDevServer(devServerOptions, compiler) + +compiler.hooks.done.tap('serve', (stats) => { + console.log() + console.log() + console.log(`App running at:`) + console.log(` - Local: ${chalk.cyan(`${protocol}://${host}:${port}`)}`) + console.log(` - Network: ${chalk.cyan(`${protocol}://${getLocalIP()}:${port}`)}`) + console.log() + + // allows livereload for webpack devserver to work without multiple instances of electron + if (electronProcess) { + manualRestart = true + } else { + manualRestart = false + // starts nodejs electron commandline browser + startElectron(devServerOptions.port) + } +}) + +server.start(port, host, (err) => { + if (err) { + process.exit(0) + } +}) diff --git a/electron/service/config/base.js b/electron/service/config/base.js new file mode 100644 index 000000000..8743f3644 --- /dev/null +++ b/electron/service/config/base.js @@ -0,0 +1,126 @@ +'use strict' + +const { DefinePlugin, EnvironmentPlugin } = require('webpack') +const { VueLoaderPlugin } = require('vue-loader') +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin') +const HTMLPlugin = require('html-webpack-plugin') +const { VuetifyPlugin } = require('webpack-plugin-vuetify') + +const resolveClientEnv = require('../utils/resolveClientEnv') +const paths = require('../utils/paths') + +const config = require('../project.config') + +const isProd = process.env.NODE_ENV === 'production' + +module.exports = { + context: process.cwd(), + + output: { + path: paths.resolve(config.outputDir), + publicPath: config.dev.publicPath, + filename: '[name].js', + }, + + resolve: { + alias: { + '@': paths.resolve('src'), + }, + extensions: ['.ts', '.tsx', '.js', '.jsx', '.vue', '.json', 'html', 'ejs'], + }, + + plugins: [ + new VueLoaderPlugin(), + new EnvironmentPlugin(['NODE_ENV']), + new CaseSensitivePathsPlugin(), + new HTMLPlugin({ + template: paths.resolve('src/index.html'), + templateParameters: { + ...resolveClientEnv( + { publicPath: isProd ? config.build.publicPath : config.dev.publicPath }, + false /* raw */ + ), + }, + }), + new VuetifyPlugin({ autoImport: true }), + new DefinePlugin({ + // vue3 feature flags + __VUE_OPTIONS_API__: 'true', + __VUE_PROD_DEVTOOLS__: 'false', + + ...resolveClientEnv({ + publicPath: isProd ? config.build.publicPath : config.dev.publicPath, + }), + }), + ], + + module: { + noParse: /^(vue|vue-router)$/, + + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + }, + // babel + { + test: /\.m?jsx?$/, + exclude: (file) => { + // always transpile js in vue files + if (/\.vue\.jsx?$/.test(file)) { + return false + } + // Don't transpile node_modules + return /node_modules/.test(file) + }, + use: ['thread-loader', 'babel-loader'], + }, + + // ts + { + test: /\.tsx?$/, + use: [ + 'thread-loader', + 'babel-loader', + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: ['\\.vue$'], + happyPackMode: true, + }, + }, + ], + }, + + // images + { + test: /\.(png|jpe?g|gif|webp)(\?.*)?$/, + type: 'asset', + generator: { filename: 'img/[contenthash:8][ext][query]' }, + }, + + // do not base64-inline SVGs. + // https://github.com/facebookincubator/create-react-app/pull/1180 + { + test: /\.(svg)(\?.*)?$/, + type: 'asset/resource', + generator: { filename: 'img/[contenthash:8][ext][query]' }, + }, + + // media + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + type: 'asset', + generator: { filename: 'media/[contenthash:8][ext][query]' }, + }, + + // fonts + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, + type: 'asset', + generator: { filename: 'fonts/[contenthash:8][ext][query]' }, + }, + ], + }, +} diff --git a/electron/service/config/css.js b/electron/service/config/css.js new file mode 100644 index 000000000..3fb5893e6 --- /dev/null +++ b/electron/service/config/css.js @@ -0,0 +1,72 @@ +'use strict' + +const MiniCssExtractPlugin = require('mini-css-extract-plugin') + +const isProd = process.env.NODE_ENV === 'production' + +const plugins = [] +if (isProd) { + const filename = 'css/[name].[contenthash:8].css' + + plugins.push( + new MiniCssExtractPlugin({ + filename, + chunkFilename: filename, + }) + ) +} + +const genStyleRules = () => { + const cssLoader = { + loader: 'css-loader', + options: { + // how many loaders before css-loader should be applied to [@import]ed resources. + // stylePostLoader injected by vue-loader + postcss-loader + importLoaders: 1 + 1, + esModule: false, // css-loader using ES Modules as default in v4, but vue-style-loader support cjs only. + }, + } + const postcssLoader = { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [require('autoprefixer')] + }, + }, + } + const extractPluginLoader = { + loader: MiniCssExtractPlugin.loader, + } + const vueStyleLoader = { + loader: 'vue-style-loader', + } + + function createCSSRule(test, loader, loaderOptions) { + const loaders = [cssLoader, postcssLoader] + + if (isProd) { + loaders.unshift(extractPluginLoader) + } else { + loaders.unshift(vueStyleLoader) + } + + if (loader) { + loaders.push({ loader, options: loaderOptions }) + } + + return { test, use: loaders } + } + + return [ + createCSSRule(/\.css$/), + createCSSRule(/\.p(ost)?css$/), + createCSSRule(/\.scss$/, 'sass-loader') + ] +} + +module.exports = { + plugins, + module: { + rules: genStyleRules(), + }, +} diff --git a/electron/service/config/dev.js b/electron/service/config/dev.js new file mode 100644 index 000000000..42a82b37b --- /dev/null +++ b/electron/service/config/dev.js @@ -0,0 +1,43 @@ +'use strict' + +const { merge } = require('webpack-merge') + +const baseWebpackConfig = require('./base') +const cssWebpackConfig = require('./css') +const config = require('../project.config') +const { ProvidePlugin, DefinePlugin } = require('webpack') + + +module.exports = merge(baseWebpackConfig, cssWebpackConfig, { + entry: { + main: './src/renderer/main.js' + }, + + mode: 'development', + + devtool: 'eval-cheap-module-source-map', + + devServer: { + watchFiles: ['src/**/*'], + historyApiFallback: { + rewrites: [{ from: /./, to: '/index.html' }], + }, + devMiddleware: { + publicPath: config.dev.publicPath, + }, + open: false, + host: '0.0.0.0', + port: 'auto', + liveReload: true, + }, + + infrastructureLogging: { + level: 'warn', + }, + + stats: { + assets: false, + modules: false, + errorDetails: false, + }, +}) diff --git a/electron/service/config/main.js b/electron/service/config/main.js new file mode 100644 index 000000000..3083dea0c --- /dev/null +++ b/electron/service/config/main.js @@ -0,0 +1,73 @@ +'use strict' + +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin') +const config = require('../project.config') + +const resolveClientEnv = require('../utils/resolveClientEnv') +const paths = require('../utils/paths') +const { merge } = require('webpack-merge') +const TerserPlugin = require('terser-webpack-plugin') +const cssWebpackConfig = require('./css') +const terserOptions = require('./terserOptions') +const isProd = process.env.NODE_ENV === 'production' + +module.exports = merge(cssWebpackConfig, { + context: process.cwd(), + mode: 'production', + entry: { + main: './src/main/index.js', + preload: './src/main/preload.js', + }, + + node: { + __dirname: false, + }, + + optimization: { + minimize: true, + minimizer: [new TerserPlugin(terserOptions())], + moduleIds: 'named', + }, + target: ['electron-main'], + + output: { + path: paths.resolve(config.outputDir), + publicPath: config.dev.publicPath, + filename: '[name].js', + }, + + resolve: { + alias: { + '@': paths.resolve('src'), + }, + extensions: ['.ts', '.tsx', '.js', '.jsx', '.vue', '.json', 'html', 'ejs'], + }, + + plugins: [ + new CaseSensitivePathsPlugin(), + ], + + module: { + noParse: /^(vue|vue-router)$/, + + rules: [ + // ts + { + test: /\.tsx?$/, + use: [ + 'thread-loader', + 'babel-loader', + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: ['\\.vue$'], + happyPackMode: true, + }, + }, + ], + }, + ], + }, +} +) \ No newline at end of file diff --git a/electron/service/config/prod.js b/electron/service/config/prod.js new file mode 100644 index 000000000..1d9e87262 --- /dev/null +++ b/electron/service/config/prod.js @@ -0,0 +1,41 @@ +'use strict' + +const { merge } = require('webpack-merge') +const TerserPlugin = require('terser-webpack-plugin') + +const baseWebpackConfig = require('./base') +const cssWebpackConfig = require('./css') +const config = require('../project.config') +const terserOptions = require('./terserOptions') + +module.exports = merge(baseWebpackConfig, cssWebpackConfig, { + mode: 'production', + + output: { + publicPath: config.build.publicPath, + }, + + optimization: { + minimize: true, + minimizer: [new TerserPlugin(terserOptions())], + moduleIds: 'deterministic', + moduleIds: 'named', + splitChunks: { + cacheGroups: { + defaultVendors: { + name: `chunk-vendors`, + test: /[\\/]node_modules[\\/]/, + priority: -10, + chunks: 'initial', + }, + common: { + name: `chunk-common`, + minChunks: 2, + priority: -20, + chunks: 'initial', + reuseExistingChunk: true, + }, + }, + }, + }, +}) diff --git a/electron/service/config/renderer.js b/electron/service/config/renderer.js new file mode 100644 index 000000000..cf3fab01a --- /dev/null +++ b/electron/service/config/renderer.js @@ -0,0 +1,45 @@ +'use strict' + +const { merge } = require('webpack-merge') +const TerserPlugin = require('terser-webpack-plugin') + +const baseWebpackConfig = require('./base') +const cssWebpackConfig = require('./css') +const config = require('../project.config') +const terserOptions = require('./terserOptions') + +module.exports = merge(baseWebpackConfig, cssWebpackConfig, { + mode: 'production', + entry: { + renderer: './src/renderer/main.js', + }, + + output: { + publicPath: config.build.publicPath, + }, + + optimization: { + minimize: true, + minimizer: [new TerserPlugin(terserOptions())], + moduleIds: 'deterministic', + moduleIds: 'named', + splitChunks: { + cacheGroups: { + defaultVendors: { + name: `chunk-vendors`, + test: /[\\/]node_modules[\\/]/, + priority: -10, + chunks: 'initial', + }, + common: { + name: `chunk-common`, + minChunks: 2, + priority: -20, + chunks: 'initial', + reuseExistingChunk: true, + }, + }, + }, + }, + target: ['electron-renderer'], +}) diff --git a/electron/service/config/terserOptions.js b/electron/service/config/terserOptions.js new file mode 100644 index 000000000..134a32588 --- /dev/null +++ b/electron/service/config/terserOptions.js @@ -0,0 +1,42 @@ +'use strict' + +module.exports = (options) => ({ + terserOptions: { + compress: { + // turn off flags with small gains to speed up minification + arrows: false, + collapse_vars: false, // 0.3kb + comparisons: false, + computed_props: false, + hoist_funs: false, + hoist_props: false, + hoist_vars: false, + inline: false, + loops: false, + negate_iife: false, + properties: false, + reduce_funcs: false, + reduce_vars: false, + switches: false, + toplevel: false, + typeofs: false, + + // a few flags with noticable gains/speed ratio + // numbers based on out of the box vendor bundle + booleans: true, // 0.7kb + if_return: true, // 0.4kb + sequences: true, // 0.7kb + unused: true, // 2.3kb + + // required features to drop conditional branches + conditionals: true, + dead_code: true, + evaluate: true, + }, + mangle: { + safari10: true, + }, + }, + // parallel: options.parallel, + extractComments: false, +}) diff --git a/electron/service/project.config.js b/electron/service/project.config.js new file mode 100644 index 000000000..7e7df9c13 --- /dev/null +++ b/electron/service/project.config.js @@ -0,0 +1,16 @@ +'use strict' + +module.exports = { + // orginal was dist + outputDir: 'dist/electron', + + dev: { + publicPath: '/', + port: 8080, + }, + + build: { + // orginal was / + publicPath: './', + }, +} diff --git a/electron/service/utils/getLocalIP.js b/electron/service/utils/getLocalIP.js new file mode 100644 index 000000000..14926ee20 --- /dev/null +++ b/electron/service/utils/getLocalIP.js @@ -0,0 +1,17 @@ +'use strict' + +const os = require('os') + +module.exports = function getLocalIP() { + const interfaces = os.networkInterfaces() + + for (const devName in interfaces) { + const iface = interfaces[devName] + for (let i = 0; i < iface.length; i++) { + const alias = iface[i] + if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { + return alias.address + } + } + } +} diff --git a/electron/service/utils/loadEnv.js b/electron/service/utils/loadEnv.js new file mode 100644 index 000000000..3c054ff68 --- /dev/null +++ b/electron/service/utils/loadEnv.js @@ -0,0 +1,39 @@ +'use strict' + +const path = require('path') +const dotenv = require('dotenv') +const dotenvExpand = require('dotenv-expand') +const { error } = require('./logger') + +module.exports = function loadEnv(mode) { + const basePath = path.resolve(process.cwd(), `.env${mode ? `.${mode}` : ``}`) + const localPath = `${basePath}.local` + + const load = (envPath) => { + try { + const env = dotenv.config({ path: envPath, debug: process.env.DEBUG }) + dotenvExpand.expand(env) + } catch (err) { + // only ignore error if file is not found + if (err.toString().indexOf('ENOENT') < 0) { + error(err) + } + } + } + + load(localPath) + load(basePath) + + // by default, NODE_ENV and BABEL_ENV are set to "development" unless mode + // is production or test. However the value in .env files will take higher + // priority. + if (mode) { + const defaultNodeEnv = mode === 'production' || mode === 'test' ? mode : 'development' + if (process.env.NODE_ENV == null) { + process.env.NODE_ENV = defaultNodeEnv + } + if (process.env.BABEL_ENV == null) { + process.env.BABEL_ENV = defaultNodeEnv + } + } +} diff --git a/electron/service/utils/logger.js b/electron/service/utils/logger.js new file mode 100644 index 000000000..0d74c52c0 --- /dev/null +++ b/electron/service/utils/logger.js @@ -0,0 +1,72 @@ +'use strict' + +const chalk = require('chalk') +const stripAnsi = require('strip-ansi') +const readline = require('readline') +const EventEmitter = require('events') + +exports.events = new EventEmitter() + +function _log(type, tag, message) { + if (process.env.VUE_CLI_API_MODE && message) { + exports.events.emit('log', { + message, + type, + tag, + }) + } +} + +const format = (label, msg) => { + return msg + .split('\n') + .map((line, i) => { + return i === 0 ? `${label} ${line}` : line.padStart(stripAnsi(label).length) + }) + .join('\n') +} + +const chalkTag = (msg) => chalk.bgBlackBright.white.dim(` ${msg} `) + +exports.log = (msg = '', tag = null) => { + tag ? console.log(format(chalkTag(tag), msg)) : console.log(msg) + _log('log', tag, msg) +} + +exports.info = (msg, tag = null) => { + console.log(format(chalk.bgBlue.black(' INFO ') + (tag ? chalkTag(tag) : ''), msg)) + _log('info', tag, msg) +} + +exports.done = (msg, tag = null) => { + console.log(format(chalk.bgGreen.black(' DONE ') + (tag ? chalkTag(tag) : ''), msg)) + _log('done', tag, msg) +} + +exports.warn = (msg, tag = null) => { + console.warn( + format(chalk.bgYellow.black(' WARN ') + (tag ? chalkTag(tag) : ''), chalk.yellow(msg)) + ) + _log('warn', tag, msg) +} + +exports.error = (msg, tag = null) => { + console.error(format(chalk.bgRed(' ERROR ') + (tag ? chalkTag(tag) : ''), chalk.red(msg))) + _log('error', tag, msg) + if (msg instanceof Error) { + console.error(msg.stack) + _log('error', tag, msg.stack) + } +} + +exports.clearConsole = (title) => { + if (process.stdout.isTTY) { + const blank = '\n'.repeat(process.stdout.rows) + console.log(blank) + readline.cursorTo(process.stdout, 0, 0) + readline.clearScreenDown(process.stdout) + if (title) { + console.log(title) + } + } +} diff --git a/electron/service/utils/paths.js b/electron/service/utils/paths.js new file mode 100644 index 000000000..a3e173c34 --- /dev/null +++ b/electron/service/utils/paths.js @@ -0,0 +1,6 @@ +'use strict' + +const path = require('path') + +// gen absolute path +exports.resolve = (...args) => path.posix.join(process.cwd(), ...args) diff --git a/electron/service/utils/resolveClientEnv.js b/electron/service/utils/resolveClientEnv.js new file mode 100644 index 000000000..4a4505a65 --- /dev/null +++ b/electron/service/utils/resolveClientEnv.js @@ -0,0 +1,11 @@ +'use strict' +const prefixRE = /^VUE_APP_/ + +module.exports = function resolveClientEnv(options, raw) { + process.env.PUBLIC_PATH = options.publicPath + + if (raw) { + return env + } + +} \ No newline at end of file diff --git a/electron/service/utils/spinner.js b/electron/service/utils/spinner.js new file mode 100644 index 000000000..d643a9338 --- /dev/null +++ b/electron/service/utils/spinner.js @@ -0,0 +1,57 @@ +'use strict' + +const ora = require('ora') +const chalk = require('chalk') + +const spinner = ora() +let lastMsg = null +let isPaused = false + +exports.logWithSpinner = (symbol, msg) => { + if (!msg) { + msg = symbol + symbol = chalk.green('✔') + } + if (lastMsg) { + spinner.stopAndPersist({ + symbol: lastMsg.symbol, + text: lastMsg.text, + }) + } + spinner.text = ' ' + msg + lastMsg = { + symbol: symbol + ' ', + text: msg, + } + spinner.start() +} + +exports.stopSpinner = (persist) => { + if (lastMsg && persist !== false) { + spinner.stopAndPersist({ + symbol: lastMsg.symbol, + text: lastMsg.text, + }) + } else { + spinner.stop() + } + lastMsg = null +} + +exports.pauseSpinner = () => { + if (spinner.isSpinning) { + spinner.stop() + isPaused = true + } +} + +exports.resumeSpinner = () => { + if (isPaused) { + spinner.start() + isPaused = false + } +} + +exports.failSpinner = (text) => { + spinner.fail(text) +} diff --git a/electron/src/index.ejs b/electron/src/index.ejs deleted file mode 100644 index 381a86c7c..000000000 --- a/electron/src/index.ejs +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - inkstitch-gui - <% if (htmlWebpackPlugin.options.nodeModules) { %> - - - <% } %> - - -
- - <% if (!process.browser) { %> - - <% } %> - - - - diff --git a/electron/src/index.html b/electron/src/index.html new file mode 100644 index 000000000..1eb6fbabf --- /dev/null +++ b/electron/src/index.html @@ -0,0 +1,20 @@ + + + + + + + +
+ + + diff --git a/electron/src/lib/api.js b/electron/src/lib/api.js index b6aa74e20..63818541d 100644 --- a/electron/src/lib/api.js +++ b/electron/src/lib/api.js @@ -6,11 +6,19 @@ * */ -const axios = require('axios') -const queryString = require('query-string') +import axios from 'axios'; +import flaskserverport from './flaskserverport.json' -var port = queryString.parse(global.location.search).port +if (flaskserverport.port === undefined) { + var theflaskport = window.inkstitchAPI.flaskport() + console.log("Installed mode") + console.log(theflaskport) +} else { + var theflaskport = flaskserverport.port + console.log("Dev mode") + console.log(theflaskport) +} -module.exports = axios.create({ - baseURL: `http://127.0.0.1:${port}/` +export const inkStitch = axios.create({ + baseURL: `http://127.0.0.1:${theflaskport}` }) diff --git a/electron/src/lib/i18n.js b/electron/src/lib/i18n.js index de56fc8f7..2a7c4f665 100644 --- a/electron/src/lib/i18n.js +++ b/electron/src/lib/i18n.js @@ -6,22 +6,33 @@ * */ -module.exports.selectLanguage = function (translations) { +export function selectLanguage(translations, flaskport) { + var port = flaskport + // get language from flask server, process in modern electron isn't exposed to renderer + const request = new XMLHttpRequest(); + request.open('GET', `http://127.0.0.1:${port}/languages`, false) + request.send(null) + var process = undefined + + if (request.status === 200) { + process = JSON.parse(request.responseText) + } // get a list of available translations var availableTranslations = ['en_US']; - for(var k in translations) availableTranslations.push(k); + for (var k in translations) availableTranslations.push(k); var lang = undefined; // get system language / Inkscape language ['LANG', 'LC_MESSAGES', 'LC_ALL', 'LANGUAGE'].forEach(language => { - if (process.env[language]) { + if (process[language]) { // split encoding information, we don't need it - var current_lang = process.env[language].split(".")[0]; + var current_lang = process[language].split('.')[0]; + if (current_lang.length == 2) { // current language has only two letters (e.g. en), // compare with available languages and if present, set to a long locale name (e.g. en_US) - lang = availableTranslations.find(elem => elem.startsWith(current_lang)); + lang = availableTranslations.find((elem) => elem.startsWith(current_lang)); } else { lang = current_lang; } @@ -33,3 +44,4 @@ module.exports.selectLanguage = function (translations) { } return lang } + diff --git a/electron/src/main/index.dev.js b/electron/src/main/index.dev.js deleted file mode 100644 index 7fc8c159c..000000000 --- a/electron/src/main/index.dev.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Authors: see git history - * - * Copyright (c) 2010 Authors - * Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. - * - */ - -/** - * This file is used specifically and only for development. It installs - * `electron-debug` & `vue-devtools`. There shouldn't be any need to - * modify this file, but it can be used to extend your development - * environment. - */ - -/* eslint-disable */ - -// Install `electron-debug` with `devtron` -require('electron-debug')({ showDevTools: true }) - -// Install `vue-devtools` -require('electron').app.on('ready', () => { - let installExtension = require('electron-devtools-installer') - installExtension.default(installExtension.VUEJS_DEVTOOLS) - .then(() => {}) - .catch(err => { - console.log('Unable to install `vue-devtools`: \n', err) - }) -}) - -// Require `main` process to boot app -require('./index') diff --git a/electron/src/main/index.js b/electron/src/main/index.js index d6f259fe1..546580eaa 100644 --- a/electron/src/main/index.js +++ b/electron/src/main/index.js @@ -8,113 +8,104 @@ 'use strict' -import {app, BrowserWindow, ipcMain, dialog, shell} from 'electron' -var fs = require('fs'); -var path = require('path'); -var tmp = require('tmp'); - +const path = require('path') +const fs = require('fs') +const tmp = require('tmp') const url = require('url') +const { app, BrowserWindow, ipcMain, dialog, shell, Menu} = require('electron') +// url for printPDF flask server which is used in development and production mode -/** - * Set `__static` path to static files in production - * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html - */ -if (process.env.NODE_ENV === 'development') { - // we were run as electron --inspect=5858 path/to/main.js - // so get rid of the first two args - console.log("args " + process.argv) - process.argv.shift() - process.argv.shift() +var port = process.env.FLASKPORT +const printPdfUrl = `http://127.0.0.1:${port}/` + +const isDev = process.env.BABEL_ENV === 'development' + +var target = null +// Finds this url in the argv array and sets to target value +if (process.argv.includes(printPdfUrl)) { + target = printPdfUrl } else { - global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\') + target = process.argv[1] || ""; } - -let mainWindow - -var target = process.argv[1] || ""; var targetURL = url.parse(target) -var winURL = null; +var winURL = null -// Print PDF will give us a full URL to a flask server, bypassing Vue entirely. // Eventually this will be migrated to Vue. if (targetURL.protocol) { - winURL = target + winURL = target } else { - if (process.env.NODE_ENV === 'development') { - winURL = `http://localhost:9080/?${targetURL.query || ""}#${targetURL.pathname || ""}` - } else { - winURL = `file://${__dirname}/index.html?${targetURL.query || ""}#${targetURL.pathname || ""}`; - } + winURL = `file://${__dirname}/index.html?${targetURL.query || ""}#${targetURL.pathname || ""}` } function createWindow() { - /** - * Initial window options - */ - mainWindow = new BrowserWindow({ - height: 563, - useContentSize: true, - width: 1000, - webPreferences: {nodeIntegration: true} - }) - - mainWindow.loadURL(winURL) - mainWindow.maximize() - - mainWindow.on('closed', () => { - mainWindow = null - }) + const mainWindow = new BrowserWindow({ + useContentSize: true, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + nodeIntegration: false, + contextIsolation: true, + }, + }) + if (isDev) { + // printPDF in development mode will have dev tools activated + // Vuejs parts of Ink/Stich will not and dev tools must be accessed though the menu of electron window + mainWindow.loadURL(winURL) + mainWindow.webContents.openDevTools() + } else { + mainWindow.loadURL(winURL) + } + // This will remove the menu from the release, in dev mode the menu is available. + if(process.platform === "darwin" && !isDev) { + Menu.setApplicationMenu(Menu.buildFromTemplate([])); + } if(process.platform === "win32" || process.platform === "linux" && !isDev) { + mainWindow.removeMenu(); + } + mainWindow.maximize() + // save to PDF + ipcMain.on('save-pdf', (event, pageSize) => { + const webContents = event.sender + const win = BrowserWindow.fromWebContents(webContents) + const saveOpt = { + title: "Save PDF", + defaultPath: "Inkstitch.pdf", + bookmark: "true", + } + win.webContents.printToPDF({}).then(pageSize => { + dialog.showSaveDialog(saveOpt).then(filename => { + const { filePath } = filename; + fs.writeFileSync(filePath, pageSize, (error) => { + if (error) { + throw error + } + console.log(`Wrote PDF successfully to ${pdfPath}`) + }) + }).catch(error => { + console.log(`Failed to write PDF to ${pdfPath}: `, error) + }) + }) + }) + // openPDF + ipcMain.on('open-pdf', (event, pageSize) => { + const webContents = event.sender + const win = BrowserWindow.fromWebContents(webContents) + win.webContents.printToPDF({}).then(pageSize => { + tmp.file({keep: true, discardDescriptor: true}, function(err, path, fd, cleanupCallback) { + fs.writeFileSync(path, pageSize, 'utf-8'); + shell.openPath(path); + }) + }) + }) } -app.on('ready', createWindow) +app.whenReady().then(() => { + createWindow() + app.on('activate', () => { + if(BrowserWindow.getAllWindows().length === 0) { + createWindow() + } + }) +}) app.on('window-all-closed', () => { app.quit() }) - -app.on('activate', () => { - if (mainWindow === null) { - createWindow() - } -}) - -ipcMain.on('save-pdf', function (event, pageSize) { - mainWindow.webContents.printToPDF({"pageSize": pageSize}, function(error, data) { - dialog.showSaveDialog(mainWindow, {"defaultPath": "inkstitch.pdf", - "filters": [{ name: 'PDF', extensions: ['pdf'] }] - }, function(filename, bookmark) { - if (typeof filename !== 'undefined') - fs.writeFileSync(filename, data, 'utf-8'); - }) - }) -}) - -ipcMain.on('open-pdf', function (event, pageSize) { - mainWindow.webContents.printToPDF({"pageSize": pageSize}, function(error, data) { - tmp.file({keep: true, discardDescriptor: true}, function(err, path, fd, cleanupCallback) { - fs.writeFileSync(path, data, 'utf-8'); - shell.openItem(path); - }) - }) -}) - - -/** - * Auto Updater - * - * Uncomment the following code below and install `electron-updater` to - * support auto updating. Code Signing with a valid certificate is required. - * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating - */ - -/* -import { autoUpdater } from 'electron-updater' - -autoUpdater.on('update-downloaded', () => { - autoUpdater.quitAndInstall() -}) - -app.on('ready', () => { - if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates() -}) - */ diff --git a/electron/src/main/preload.js b/electron/src/main/preload.js new file mode 100644 index 000000000..b7d1c02a9 --- /dev/null +++ b/electron/src/main/preload.js @@ -0,0 +1,7 @@ +const { contextBridge, ipcRenderer } = require ('electron') + +contextBridge.exposeInMainWorld('inkstitchAPI', { + savepdf: (pageSize) => { ipcRenderer.send('save-pdf', pageSize) }, + openpdf: (pageSize) => { ipcRenderer.send('open-pdf', pageSize) }, + flaskport: () => process.env.FLASKPORT, +}) diff --git a/electron/src/renderer/App.vue b/electron/src/renderer/App.vue index 2e5bbb9d3..b98b13999 100644 --- a/electron/src/renderer/App.vue +++ b/electron/src/renderer/App.vue @@ -9,20 +9,9 @@ - - - diff --git a/electron/src/renderer/assets/js/simulator.js b/electron/src/renderer/assets/js/simulator.js index 186e3e303..8a0423f36 100644 --- a/electron/src/renderer/assets/js/simulator.js +++ b/electron/src/renderer/assets/js/simulator.js @@ -5,19 +5,18 @@ * Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. * */ +import { inkStitch } from '../../../lib/api.js' -const inkStitch = require("../../../lib/api") -const Mousetrap = require("mousetrap") import { SVG } from '@svgdotjs/svg.js' -require('@svgdotjs/svg.panzoom.js/src/svg.panzoom.js') -require('@svgdotjs/svg.filter.js') -const svgpath = require('svgpath') +import '@svgdotjs/svg.panzoom.js' +import '@svgdotjs/svg.filter.js' +import svgpath from 'svgpath' import Loading from 'vue-loading-overlay'; import 'vue-loading-overlay/dist/vue-loading.css'; +import { reactive, toRefs } from 'vue' import VueSlider from 'vue-slider-component' -import 'vue-slider-component/theme/default.css' - -const throttle = require('lodash.throttle') +import 'vue-slider-component/theme/antd.css' +import throttle from 'lodash/throttle' function SliderMark(command, icon) { this.label = "" @@ -31,6 +30,10 @@ export default { Loading, VueSlider }, + setup() { + const data = reactive({ value: 0 }) + return toRefs(data) + }, data: function () { return { loading: false, @@ -511,15 +514,23 @@ export default { }, generatePage () { this.$refs.simulator.style.backgroundColor = this.page_specs.deskcolor + let page = this.svg.rect(this.page_specs.width, this.page_specs.height) .move(-this.stitchPlan.bounding_box[0],-this.stitchPlan.bounding_box[1]) .fill(this.page_specs.pagecolor) - .stroke({width: 1, color: 'black'}) - .filterWith(add => { - let blur = add.offset(2,2).in(add.$sourceAlpha).gaussianBlur(2) - add.blend(add.$source, blur) - }) + .stroke({width: 0.1, color: this.page_specs.bordercolor}) .back() + + if (this.page_specs.showpageshadow === "true") { + let shadow = this.svg.rect(this.page_specs.width, this.page_specs.height) + .move(-this.stitchPlan.bounding_box[0],-this.stitchPlan.bounding_box[1]) + .fill(this.page_specs.bordercolor) + .filterWith(add => { + let blur = add.offset(.5,.5).in(add.$source).gaussianBlur(.5) + }) + .back() + } + this.page_specs["bbox"] = page.bbox() }, zoomDesign () { @@ -606,29 +617,30 @@ export default { this.generateColorSections() this.generateScale() this.generateCursor() - + this.resizeCursor() + this.loading = false // v-on:keydown doesn't seem to work, maybe an Electron issue? - Mousetrap.bind("up", this.animationSpeedUp) - Mousetrap.bind("down", this.animationSlowDown) - Mousetrap.bind("left", this.animationReverse) - Mousetrap.bind("right", this.animationForward) - Mousetrap.bind("pagedown", this.animationPreviousCommand) - Mousetrap.bind("pageup", this.animationNextCommand) - Mousetrap.bind("space", this.toggleAnimation) - Mousetrap.bind("+", this.animationForwardOneStitch) - Mousetrap.bind("-", this.animationBackwardOneStitch) - Mousetrap.bind("]", this.zoomDesign) - Mousetrap.bind("[", this.zoomPage) + this.$mousetrap.bind("up", this.animationSpeedUp) + this.$mousetrap.bind("down", this.animationSlowDown) + this.$mousetrap.bind("left", this.animationReverse) + this.$mousetrap.bind("right", this.animationForward) + this.$mousetrap.bind("pagedown", this.animationPreviousCommand) + this.$mousetrap.bind("pageup", this.animationNextCommand) + this.$mousetrap.bind("space", this.toggleAnimation) + this.$mousetrap.bind("+", this.animationForwardOneStitch) + this.$mousetrap.bind("-", this.animationBackwardOneStitch) + this.$mousetrap.bind("]", this.zoomDesign) + this.$mousetrap.bind("[", this.zoomPage) this.svg.on('zoom', this.resizeCursor) - this.resizeCursor() inkStitch.get('page_specs').then(response => { this.page_specs = response.data this.generatePage() }) + this.start() }) } diff --git a/electron/src/renderer/assets/style/simulator.css b/electron/src/renderer/assets/style/simulator.css index 64047a7ef..e938cbe3a 100644 --- a/electron/src/renderer/assets/style/simulator.css +++ b/electron/src/renderer/assets/style/simulator.css @@ -5,11 +5,10 @@ * Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. * */ - -* { - box-sizing: border-box; + * { + padding: 1px; + margin: 1px; } - .loading-icon { text-align: center; margin-bottom: 1rem; @@ -32,7 +31,7 @@ button { align-items: flex-start; text-align: center; cursor: default; - background-color: buttonface; + background: linear-gradient(0deg, rgba(169,169,169,1) 0%, rgba(255,255,255,1) 68%, rgba(227,227,227,1) 100%); box-sizing: border-box; padding: 2px 6px 3px; border-width: 2px; @@ -183,7 +182,7 @@ button.pressed { } .vue-slider { - height: 25px !important; + height: 10px !important; margin-bottom: 14px; } @@ -203,7 +202,7 @@ button.pressed { margin-right: 10px; } -.slider-container::v-deep .vue-slider-mark-step { +.slider-container :deep(.vue-slider-mark-step) { width: 2px; height: 20px; border-radius: 2px; @@ -212,26 +211,26 @@ button.pressed { box-shadow: inset 1px 0 1px #ffffffbd, inset -1px 0 1px black; } -.slider-container::v-deep .vue-slider-mark:first-child .vue-slider-mark-step, -.slider-container::v-deep .vue-slider-mark:last-child .vue-slider-mark-step { +.slider-container :deep(.vue-slider-mark:first-child .vue-slider-mark-step), +.slider-container :deep(.vue-slider-mark:last-child .vue-slider-mark-step) { display: block; } -.slider-container::v-deep .vue-slider-rail { +.slider-container :deep(.vue-slider-rail) { border: 1px solid #dedede; } -.slider-container::v-deep .vue-slider-process { +.slider-container :deep(.vue-slider-process) { border-radius: 0; box-shadow: inset 0px 2px 2px #ffffff80, inset 0px -2px 2px #0000002e; } -.slider-container::v-deep .vue-slider-process:first-child { +.slider-container :deep(.vue-slider-process:first-child) { border-top-left-radius: 15px; border-bottom-left-radius: 15px; } -.slider-container::v-deep .vue-slider-process:nth-last-child(3) { +.slider-container :deep(.vue-slider-process:nth-last-child(3)) { border-top-right-radius: 15px; border-bottom-right-radius: 15px; } @@ -247,20 +246,20 @@ button.pressed { } .slider-label-trim { - transform: scale(1, 0.75) translate(-50%, -9px); + transform: scale(1, 0.75) translate(-50%, -9px) !important;; } .slider-label-stop { font-size: 1.2rem; - transform: translate(-50%, 12px); + transform: translate(-50%, 12px) !important;; } .slider-label-jump { - transform: translate(-50%, -50px); + transform: translate(-50%, -50px) !important;; } .slider-label-color-change { - transform: translate(-50%, 10px); + transform: translate(-50%, 10px) !important;; } .current-stitch-input { @@ -286,23 +285,23 @@ button.pressed { /* we need ::v-deep here because the svg tag is added by svg.js and so Vue can't add the data-v-* attribute */ -div.simulator::v-deep svg.simulation { +div.simulator :deep(svg.simulation) { flex-grow: 1; flex-shrink: 1; order: -2; margin-bottom: -48px; } -div.simulator::v-deep svg.simulation-scale { +div.simulator :deep(svg.simulation-scale) { height: 50px; order: -1; margin-left: 5px; } -div.simulator::v-deep .simulation-scale-label { +div.simulator :deep(.simulation-scale-label) { font-size: 14px; } -div.simulator::v-deep .cursor { +div.simulator :deep(.cursor) { /* not sure what to add here to make it more visible */ } diff --git a/electron/src/renderer/components/InstallPalettes.vue b/electron/src/renderer/components/InstallPalettes.vue deleted file mode 100644 index 3608f858f..000000000 --- a/electron/src/renderer/components/InstallPalettes.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - diff --git a/electron/src/renderer/components/NotFound.vue b/electron/src/renderer/components/NotFound.vue new file mode 100644 index 000000000..4accf9019 --- /dev/null +++ b/electron/src/renderer/components/NotFound.vue @@ -0,0 +1,12 @@ + + + diff --git a/electron/src/renderer/components/Preferences.vue b/electron/src/renderer/components/Preferences.vue index f68c942d2..101f1b8a8 100644 --- a/electron/src/renderer/components/Preferences.vue +++ b/electron/src/renderer/components/Preferences.vue @@ -1,13 +1,22 @@ + +