|
@ -30,18 +30,6 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-20.04-pip-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
|
|
@ -57,8 +57,7 @@ jobs:
|
|||
# files when a new translation string is added but we don't need to
|
||||
# commit those until folks actually translate the new strings.
|
||||
if git diff translations | grep -qE '^[-+]msgstr ".+"$'; then
|
||||
make electron/src/renderer/assets/translations.json
|
||||
git add translations electron/src/renderer/assets/translations.json
|
||||
git add translations
|
||||
git commit -m "new translations from Crowdin"
|
||||
git push https://github.com/inkstitch/inkstitch main
|
||||
fi
|
||||
|
|
|
@ -18,8 +18,6 @@ locales/
|
|||
/src/
|
||||
.DS_STORE
|
||||
.DS_Store
|
||||
flaskserverport.json
|
||||
electron/yarn.lock
|
||||
|
||||
# debug and profile files
|
||||
/DEBUG.ini
|
||||
|
|
|
@ -8,7 +8,6 @@ Feel free to find something that interests you. If you're looking for ideas, co
|
|||
* **please read our [coding style guide](CODING_STYLE.md)**
|
||||
* build / CI system (GitHub actions)
|
||||
* we need someone to figure out how we can start code-signing our application
|
||||
* web design (electron UI)
|
||||
* translations ([how to translate](https://github.com/inkstitch/inkstitch/blob/main/LOCALIZATION.md))
|
||||
* issue wrangling
|
||||
* combining duplicate issues
|
||||
|
|
14
Makefile
|
@ -2,18 +2,16 @@
|
|||
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 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
|
||||
rm -rf build dist inx locales artifacts win mac *.spec *.tar.gz *.zip
|
||||
|
||||
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 ..
|
||||
make inx
|
||||
|
||||
.PHONY: inx
|
||||
inx: version locales
|
||||
|
@ -33,13 +31,7 @@ messages.po: inx
|
|||
rm -rf src/
|
||||
pybabel extract -o messages-babel.po -F babel.conf --add-location=full --add-comments=l10n,L10n,L10N --sort-by-file --strip-comments -k N_ -k '$$gettext' .
|
||||
rm pyembroidery-format-descriptions.py inkstitch-fonts-metadata.py inkstitch-tiles-metadata.py
|
||||
cd electron && yarn --link-duplicates --pure-lockfile
|
||||
find electron/src -name '*.html' -o -name '*.js' -o -name '*.vue' | xargs electron/node_modules/.bin/gettext-extract --quiet --attribute v-translate --output messages-vue.po
|
||||
msgcat -o messages.po messages-babel.po messages-vue.po messages-inx.po
|
||||
|
||||
electron/src/renderer/assets/translations.json: $(wildcard translations/messages_*.po)
|
||||
find translations -name '*.po' -a ! -empty | \
|
||||
xargs electron/node_modules/.bin/gettext-compile --output electron/src/renderer/assets/translations.json
|
||||
msgcat -o messages.po messages-babel.po messages-inx.po
|
||||
|
||||
%.po: %.mo
|
||||
msgunfmt -o $@ $<
|
||||
|
|
|
@ -4,21 +4,13 @@ ARCH="$(uname -m)"
|
|||
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
|
||||
cp -a icons locales print LICENSE VERSION palettes symbols fonts tiles dbus inx dist/inkstitch.app/Contents/Resources
|
||||
# adding version to 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/"
|
||||
# Checking arch of macos and setting path of electron for arm64 or intel
|
||||
echo "Checking for macOS arch."
|
||||
if [ "${ARCH}" = "arm64" ]; then
|
||||
ELECTRON_BUILD_PATH="electron/build/mac-arm64"
|
||||
echo "found arm64"
|
||||
else
|
||||
ELECTRON_BUILD_PATH="electron/build/mac"
|
||||
echo "found intel"
|
||||
fi
|
||||
|
||||
# inside the scripts folder are:
|
||||
# - preinstaller (checks for previously installed inkstitch and deletes it, Inkscape check with error message) and
|
||||
# - postinstaller (moves inkstitch folder from /tmp to user Inkscape extensions folder in $HOME)
|
||||
|
@ -33,8 +25,6 @@ if [ "$BUILD" = "osx" ]; then
|
|||
# This code signs and notarize the inkstitch.app
|
||||
DEV_IDENT="Developer ID Application: Lex Neva (929A568N58)"
|
||||
echo "Signing of inkstitch.app"
|
||||
# Coyping inkstitch-gui.app into inkstitch
|
||||
ditto "${ELECTRON_BUILD_PATH}" dist/inkstitch.app/Contents/Frameworks/electron
|
||||
# signing the binary may fix notary issue
|
||||
/usr/bin/codesign -s "${DEV_IDENT}" \
|
||||
--deep \
|
||||
|
@ -55,7 +45,6 @@ if [ "$BUILD" = "osx" ]; then
|
|||
INSTALLER_IDENT="Developer ID Installer: Lex Neva (929A568N58)"
|
||||
/usr/bin/pkgbuild --root dist/inkstitch.app \
|
||||
-s "${INSTALLER_IDENT}" \
|
||||
--component-plist installer_scripts/inkstitch.plist \
|
||||
--ownership recommended \
|
||||
--identifier org.inkstitch.installer \
|
||||
--version ${VERSION} \
|
||||
|
@ -90,9 +79,7 @@ if [ "$BUILD" = "osx" ]; then
|
|||
fi
|
||||
else
|
||||
# local builds will not be signed or notarized
|
||||
cp -a "${ELECTRON_BUILD_PATH}" dist/inkstitch.app/Contents/Frameworks/electron
|
||||
pkgbuild --root dist/inkstitch.app \
|
||||
--component-plist installer_scripts/inkstitch.plist \
|
||||
--ownership recommended \
|
||||
--identifier org.inkstitch.installer \
|
||||
--version ${VERSION} \
|
||||
|
@ -107,7 +94,6 @@ if [ "$BUILD" = "osx" ]; then
|
|||
else
|
||||
cp -a images/examples palettes symbols fonts tiles dbus inx LICENSE VERSION dist/inkstitch
|
||||
cp -a icons locales print dist/inkstitch/bin
|
||||
cp -a electron/build/*-unpacked dist/inkstitch/electron
|
||||
fi
|
||||
|
||||
if [ "$BUILD" = "windows" ]; then
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
#!/bin/bash
|
||||
ARCH=$(python -c "import platform; n = platform.architecture()[0]; print(n)")
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ "$BUILD" = "windows" ]; then
|
||||
if [ "$ARCH" = "32bit" ]; then
|
||||
args="-w --ia32"
|
||||
else
|
||||
args="-w --x64"
|
||||
fi
|
||||
elif [ "$BUILD" = "linux" ]; then
|
||||
args="-l"
|
||||
elif [ "$BUILD" = "osx" ]; then
|
||||
cp installer_scripts/electron-entitlements.plist electron/build/
|
||||
args="-m"
|
||||
fi
|
||||
|
||||
# electron version setting on release
|
||||
if [[ "$VERSION" =~ ^v[0-9][.0-9]+$ ]]; then
|
||||
sed -i'' -e 's/11.99.11/'"${VERSION#v}"'/' electron/package.json
|
||||
fi
|
||||
cd electron
|
||||
which yarn > /dev/null 2>&1 || npm install -g yarn
|
||||
yarn --link-duplicates --pure-lockfile
|
||||
yarn run dist ${args}
|
|
@ -17,7 +17,7 @@ pyinstaller_args+="--log-level DEBUG "
|
|||
# This adds bundle identifier in reverse DSN format for macos
|
||||
if [ "$BUILD" = "osx" ]; then
|
||||
pyinstaller_args+="--osx-bundle-identifier org.inkstitch.app "
|
||||
pyinstaller_args+="-i electron/build/icons/mac/inkstitch.icns "
|
||||
pyinstaller_args+="-i images/inkstitch/mac/inkstitch.icns "
|
||||
if [[ -z ${GITHUB_REF} ]]; then
|
||||
echo "Dev or Local Build"
|
||||
else
|
||||
|
@ -43,11 +43,11 @@ if [ "$BUILD" = "windows" ]; then
|
|||
done;
|
||||
sed -i'' 's/3, 2, 1,/'"${INFO_VERSION[0]}, ${INFO_VERSION[1]}, ${INFO_VERSION[2]},"'/' installer_scripts/file_version_info.txt
|
||||
fi
|
||||
# set year and version in version_info
|
||||
# set year and version in version_info
|
||||
sed -i'' 's/1.1.1/'"${VERSION#v}"'/' installer_scripts/file_version_info.txt
|
||||
sed -i'' 's/1234/'"${info_year}"'/' installer_scripts/file_version_info.txt
|
||||
# sets icon to inkstitch.exe
|
||||
pyinstaller_args+="--i electron/build/icons/win/inkstitch.ico "
|
||||
# sets icon to inkstitch.exe
|
||||
pyinstaller_args+="-i images/inkstitch/win/inkstitch.ico "
|
||||
pyinstaller_args+="--version-file installer_scripts/file_version_info.txt "
|
||||
python -m PyInstaller $pyinstaller_args inkstitch.py
|
||||
elif [ "$BUILD" = "osx" ]; then
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/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)
|
|
@ -5,4 +5,4 @@
|
|||
# Instead of files, "--diff" may be passed to check only the lines changed
|
||||
# by a diff piped to standard input.
|
||||
|
||||
flake8 --count --max-complexity=10 --max-line-length=150 --statistics --exclude=pyembroidery,__init__.py,electron,build,src,dist,./*-metadata.py,./pyembroidery-format-descriptions.py "${@:-.}"
|
||||
flake8 --count --max-complexity=10 --max-line-length=150 --statistics --exclude=pyembroidery,__init__.py,build,src,dist,./*-metadata.py,./pyembroidery-format-descriptions.py "${@:-.}"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
# https://github.com/browserslist/browserslist#queries
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
|
@ -1 +0,0 @@
|
|||
* text=auto eol=lf
|
|
@ -1,10 +0,0 @@
|
|||
.DS_Store
|
||||
dist/electron/*
|
||||
dist/web/*
|
||||
build/*
|
||||
!build/icons
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
npm-debug.log.*
|
||||
thumbs.db
|
||||
!.gitkeep
|
|
@ -1,6 +0,0 @@
|
|||
Electron UI
|
||||
===========
|
||||
|
||||
This is the home of the future Electron-based UI. Currently only the Print extension uses Electron.
|
||||
|
||||
For local development, run `yarn` in this directory to install dependencies and build Electron.
|
|
@ -1,16 +0,0 @@
|
|||
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/],
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
{
|
||||
"name": "inkstitch-gui",
|
||||
"productName": "inkstitch-gui",
|
||||
"version": "11.99.11",
|
||||
"description": "Ink/Stitch GUI",
|
||||
"main": "./dist/electron/main.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"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",
|
||||
"appId": "org.inkstitch.gui",
|
||||
"directories": {
|
||||
"output": "build"
|
||||
},
|
||||
"files": [
|
||||
"dist/electron/**/*"
|
||||
],
|
||||
"linux": {
|
||||
"icon": "build/icons",
|
||||
"target": [
|
||||
{
|
||||
"target": "dir"
|
||||
}
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"icon": "build/icons/win/inkstitch.ico",
|
||||
"target": "dir"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "build/icons/mac/inkstitch.icns",
|
||||
"target": [
|
||||
{
|
||||
"target": "dir"
|
||||
}
|
||||
],
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"strictVerify": false,
|
||||
"entitlements": "build/electron-entitlements.plist",
|
||||
"entitlementsInherit": "build/electron-entitlements.plist"
|
||||
}
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "lex",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"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.3.0",
|
||||
"webpack-plugin-vuetify": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv-expand": "^8.0.3",
|
||||
"easygettext": "^2.17.0",
|
||||
"electron": "14.2.9",
|
||||
"electron-builder": "^23.0.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"
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
'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')
|
||||
})
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
'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')
|
||||
})
|
||||
})
|
|
@ -1,102 +0,0 @@
|
|||
'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)
|
||||
}
|
||||
})
|
|
@ -1,126 +0,0 @@
|
|||
'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 <http://link.vuejs.org/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]' },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
'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(),
|
||||
},
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
'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,
|
||||
},
|
||||
})
|
|
@ -1,73 +0,0 @@
|
|||
'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,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
|
@ -1,41 +0,0 @@
|
|||
'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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
|
@ -1,45 +0,0 @@
|
|||
'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'],
|
||||
})
|
|
@ -1,42 +0,0 @@
|
|||
'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,
|
||||
})
|
|
@ -1,16 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
// orginal was dist
|
||||
outputDir: 'dist/electron',
|
||||
|
||||
dev: {
|
||||
publicPath: '/',
|
||||
port: 8080,
|
||||
},
|
||||
|
||||
build: {
|
||||
// orginal was /
|
||||
publicPath: './',
|
||||
},
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
'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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
'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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
'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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
// gen absolute path
|
||||
exports.resolve = (...args) => path.posix.join(process.cwd(), ...args)
|
|
@ -1,11 +0,0 @@
|
|||
'use strict'
|
||||
const prefixRE = /^VUE_APP_/
|
||||
|
||||
module.exports = function resolveClientEnv(options, raw) {
|
||||
process.env.PUBLIC_PATH = options.publicPath
|
||||
|
||||
if (raw) {
|
||||
return env
|
||||
}
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
'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)
|
||||
}
|
|
@ -1,20 +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.
|
||||
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
global = globalThis
|
||||
</script>
|
|
@ -1,24 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import flaskserverport from './flaskserverport.json'
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
export const inkStitch = axios.create({
|
||||
baseURL: `http://127.0.0.1:${theflaskport}`
|
||||
})
|
|
@ -1,47 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
var lang = undefined;
|
||||
|
||||
// get system language / Inkscape language
|
||||
['LANG', 'LC_MESSAGES', 'LC_ALL', 'LANGUAGE'].forEach(language => {
|
||||
if (process[language]) {
|
||||
// split encoding information, we don't need it
|
||||
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));
|
||||
} else {
|
||||
lang = current_lang;
|
||||
}
|
||||
}
|
||||
})
|
||||
// set default language
|
||||
if (lang === undefined) {
|
||||
lang = "en_US"
|
||||
}
|
||||
return lang
|
||||
}
|
||||
|
|
@ -1,112 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
target = process.argv[1] || "";
|
||||
}
|
||||
var targetURL = url.parse(target)
|
||||
var winURL = null
|
||||
|
||||
// Eventually this will be migrated to Vue.
|
||||
if (targetURL.protocol) {
|
||||
winURL = target
|
||||
} else {
|
||||
winURL = `file://${__dirname}/index.html?${targetURL.query || ""}#${targetURL.pathname || ""}`
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
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",
|
||||
filters: [{ name: 'PDF', extensions: ['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.whenReady().then(() => {
|
||||
createWindow()
|
||||
app.on('activate', () => {
|
||||
if(BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit()
|
||||
})
|
|
@ -1,7 +0,0 @@
|
|||
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,
|
||||
})
|
|
@ -1,17 +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.
|
||||
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons');
|
||||
</style>
|
|
@ -1,654 +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.
|
||||
*
|
||||
*/
|
||||
import { inkStitch } from '../../../lib/api.js'
|
||||
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
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/antd.css'
|
||||
import throttle from 'lodash/throttle'
|
||||
|
||||
function SliderMark(command, icon) {
|
||||
this.label = ""
|
||||
this.command = command
|
||||
this.icon = icon
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'simulator',
|
||||
components: {
|
||||
Loading,
|
||||
VueSlider
|
||||
},
|
||||
setup() {
|
||||
const data = reactive({ value: 0 })
|
||||
return toRefs(data)
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
loading: false,
|
||||
controlsExpanded: true,
|
||||
infoExpanded: false,
|
||||
infoMaxHeight: 0,
|
||||
speed: 16,
|
||||
currentStitch: 1,
|
||||
currentStitchDisplay: 1,
|
||||
direction: 1,
|
||||
numStitches: 1,
|
||||
animating: false,
|
||||
sliderProcess: dotPos => this.sliderColorSections,
|
||||
showTrims: false,
|
||||
showJumps: false,
|
||||
showColorChanges: false,
|
||||
showStops: false,
|
||||
showNeedlePenetrationPoints: false,
|
||||
renderJumps: true,
|
||||
showRealisticPreview: false,
|
||||
showCursor: true,
|
||||
error: false,
|
||||
error_message: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentStitch: throttle(function () {
|
||||
this.currentStitchDisplay = Math.floor(this.currentStitch)
|
||||
}, 100, {leading: true, trailing: true}),
|
||||
showNeedlePenetrationPoints: function () {
|
||||
if (this.needlePenetrationPoints === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.needlePenetrationPoints.forEach(npp => {
|
||||
if (this.showNeedlePenetrationPoints) {
|
||||
npp.show()
|
||||
} else {
|
||||
npp.hide()
|
||||
}
|
||||
})
|
||||
},
|
||||
renderJumps() {
|
||||
this.renderedStitch = 1
|
||||
this.renderFrame()
|
||||
},
|
||||
showRealisticPreview() {
|
||||
let animating = this.animating
|
||||
this.stop()
|
||||
|
||||
if (this.showRealisticPreview) {
|
||||
if (this.realisticPreview === null) {
|
||||
// This workflow should be improved and might be a bit unconventional.
|
||||
// We don't want to make the user wait for it too long.
|
||||
// It would be best, if the realistic preview could load before it is actually requested.
|
||||
this.$nextTick(() => {this.loading=true})
|
||||
setImmediate(()=> {this.generateRealisticPaths()})
|
||||
setImmediate(()=> {this.loading = false})
|
||||
}
|
||||
|
||||
setImmediate(()=> {
|
||||
this.renderedStitch = 0
|
||||
this.renderFrame()
|
||||
|
||||
this.simulation.hide()
|
||||
this.realisticPreview.show()
|
||||
})
|
||||
|
||||
} else {
|
||||
this.renderedStitch = 0
|
||||
this.renderFrame()
|
||||
|
||||
this.simulation.show()
|
||||
this.realisticPreview.hide()
|
||||
}
|
||||
|
||||
if (animating) {
|
||||
this.start()
|
||||
}
|
||||
},
|
||||
showCursor: function () {
|
||||
if (this.showCursor) {
|
||||
this.cursor.show()
|
||||
} else {
|
||||
this.cursor.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
speedDisplay() {
|
||||
return this.speed * this.direction
|
||||
},
|
||||
currentCommand() {
|
||||
let stitch = this.stitches[Math.floor(this.currentStitch)]
|
||||
|
||||
if (stitch === undefined || stitch === null) {
|
||||
return ""
|
||||
}
|
||||
|
||||
let label = this.$gettext("STITCH")
|
||||
switch (true) {
|
||||
case stitch.jump:
|
||||
label = this.$gettext("JUMP")
|
||||
break
|
||||
case stitch.trim:
|
||||
label = this.$gettext("TRIM")
|
||||
break
|
||||
case stitch.stop:
|
||||
label = this.$gettext("STOP")
|
||||
break
|
||||
case stitch.color_change:
|
||||
label = this.$gettext("COLOR CHANGE")
|
||||
break
|
||||
}
|
||||
|
||||
return label
|
||||
},
|
||||
paused() {
|
||||
return !this.animating
|
||||
},
|
||||
forward() {
|
||||
return this.direction > 0
|
||||
},
|
||||
reverse() {
|
||||
return this.direction < 0
|
||||
},
|
||||
sliderMarks() {
|
||||
var marks = {}
|
||||
|
||||
if (this.showTrims)
|
||||
Object.assign(marks, this.trimMarks);
|
||||
|
||||
if (this.showJumps)
|
||||
Object.assign(marks, this.jumpMarks);
|
||||
|
||||
if (this.showColorChanges)
|
||||
Object.assign(marks, this.colorChangeMarks);
|
||||
|
||||
if (this.showStops)
|
||||
Object.assign(marks, this.stopMarks);
|
||||
|
||||
return marks
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleInfo() {
|
||||
this.infoExpanded = !this.infoExpanded;
|
||||
this.infoMaxHeight = this.$refs.controlInfoButton.getBoundingClientRect().top;
|
||||
},
|
||||
toggleControls() {
|
||||
this.controlsExpanded = !this.controlsExpanded;
|
||||
},
|
||||
animationSpeedUp() {
|
||||
this.speed *= 2.0
|
||||
},
|
||||
animationSlowDown() {
|
||||
this.speed = Math.max(this.speed / 2.0, 1)
|
||||
},
|
||||
animationReverse() {
|
||||
this.direction = -1
|
||||
this.start()
|
||||
},
|
||||
animationForward() {
|
||||
this.direction = 1
|
||||
this.start()
|
||||
},
|
||||
toggleAnimation(e) {
|
||||
if (this.animating) {
|
||||
this.stop()
|
||||
} else {
|
||||
this.start()
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
},
|
||||
animationForwardOneStitch() {
|
||||
this.setCurrentStitch(this.currentStitch + 1)
|
||||
},
|
||||
animationBackwardOneStitch() {
|
||||
this.setCurrentStitch(this.currentStitch - 1)
|
||||
},
|
||||
animationNextCommand() {
|
||||
let nextCommandIndex = this.getNextCommandIndex()
|
||||
if (nextCommandIndex === -1) {
|
||||
this.setCurrentStitch(this.stitches.length)
|
||||
} else {
|
||||
this.setCurrentStitch(this.commandList[nextCommandIndex])
|
||||
}
|
||||
},
|
||||
animationPreviousCommand() {
|
||||
let nextCommandIndex = this.getNextCommandIndex()
|
||||
let prevCommandIndex = 0
|
||||
if (nextCommandIndex === -1) {
|
||||
prevCommandIndex = this.commandList.length - 2
|
||||
} else {
|
||||
prevCommandIndex = nextCommandIndex - 2
|
||||
}
|
||||
let previousCommand = this.commandList[prevCommandIndex]
|
||||
if (previousCommand === undefined) {
|
||||
previousCommand = 1
|
||||
}
|
||||
this.setCurrentStitch(previousCommand)
|
||||
},
|
||||
getNextCommandIndex() {
|
||||
let currentStitch = this.currentStitchDisplay
|
||||
let nextCommand = this.commandList.findIndex(function (command) {
|
||||
return command > currentStitch
|
||||
})
|
||||
return nextCommand
|
||||
},
|
||||
onCurrentStitchEntered() {
|
||||
let newCurrentStitch = parseInt(this.$refs.currentStitchInput.value)
|
||||
|
||||
if (isNaN(newCurrentStitch)) {
|
||||
this.$refs.currentStitchInput.value = Math.floor(this.currentStitch)
|
||||
} else {
|
||||
this.setCurrentStitch(parseInt(newCurrentStitch))
|
||||
}
|
||||
},
|
||||
setCurrentStitch(newCurrentStitch) {
|
||||
this.stop()
|
||||
this.currentStitch = newCurrentStitch
|
||||
this.clampCurrentStitch()
|
||||
this.renderFrame()
|
||||
},
|
||||
clampCurrentStitch() {
|
||||
this.currentStitch = Math.max(Math.min(this.currentStitch, this.numStitches), 0)
|
||||
},
|
||||
animate() {
|
||||
let frameStart = performance.now()
|
||||
let frameTime = null
|
||||
|
||||
if (this.lastFrameStart !== null) {
|
||||
frameTime = frameStart - this.lastFrameStart
|
||||
} else {
|
||||
frameTime = this.targetFramePeriod
|
||||
}
|
||||
|
||||
this.lastFrameStart = frameStart
|
||||
|
||||
let numStitches = this.speed * Math.max(frameTime, this.targetFramePeriod) / 1000.0;
|
||||
this.currentStitch = this.currentStitch + numStitches * this.direction
|
||||
this.clampCurrentStitch()
|
||||
|
||||
this.renderFrame()
|
||||
|
||||
if (this.animating && this.shouldAnimate()) {
|
||||
this.timer = setTimeout(this.animate, Math.max(0, this.targetFramePeriod - frameTime))
|
||||
} else {
|
||||
this.timer = null;
|
||||
this.stop()
|
||||
}
|
||||
},
|
||||
renderFrame() {
|
||||
while (this.renderedStitch < this.currentStitch) {
|
||||
this.renderedStitch += 1
|
||||
if (!this.renderJumps && this.stitches[this.renderedStitch].jump){
|
||||
if (this.showRealisticPreview) {
|
||||
this.realisticPaths[this.renderedStitch].hide();
|
||||
} else {
|
||||
this.stitchPaths[this.renderedStitch].hide();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (this.showRealisticPreview) {
|
||||
this.realisticPaths[this.renderedStitch].show()
|
||||
} else {
|
||||
this.stitchPaths[this.renderedStitch].show();
|
||||
}
|
||||
}
|
||||
|
||||
while (this.renderedStitch > this.currentStitch) {
|
||||
if (this.showRealisticPreview) {
|
||||
this.realisticPaths[this.renderedStitch].hide()
|
||||
} else {
|
||||
this.stitchPaths[this.renderedStitch].hide();
|
||||
}
|
||||
this.renderedStitch -= 1
|
||||
}
|
||||
|
||||
this.moveCursor()
|
||||
},
|
||||
shouldAnimate() {
|
||||
if (this.direction == 1 && this.currentStitch < this.numStitches) {
|
||||
return true;
|
||||
} else if (this.direction == -1 && this.currentStitch > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
start() {
|
||||
if (!this.animating && this.shouldAnimate()) {
|
||||
this.animating = true
|
||||
this.timer = setTimeout(this.animate, 0);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
if (this.animating) {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
this.animating = false
|
||||
this.lastFrameStart = null
|
||||
}
|
||||
},
|
||||
resizeCursor() {
|
||||
// This makes the cursor stay the same size when zooming in or out.
|
||||
// I'm not exactly sure how it works, but it does.
|
||||
this.cursor.size(25 / this.svg.zoom())
|
||||
this.cursor.stroke({width: 2 / this.svg.zoom()})
|
||||
|
||||
// SVG.js seems to move the cursor when we resize it, so we need to put
|
||||
// it back where it goes.
|
||||
this.moveCursor()
|
||||
|
||||
this.adjustScale()
|
||||
},
|
||||
moveCursor() {
|
||||
let stitch = this.stitches[Math.floor(this.currentStitch)]
|
||||
if (stitch === null || stitch === undefined) {
|
||||
this.cursor.hide()
|
||||
} else if (this.showCursor) {
|
||||
this.cursor.show()
|
||||
this.cursor.center(stitch.x, stitch.y)
|
||||
}
|
||||
},
|
||||
adjustScale: throttle(function () {
|
||||
let one_mm = 96 / 25.4 * this.svg.zoom();
|
||||
let scaleWidth = one_mm
|
||||
let simulatorWidth = this.$refs.simulator.getBoundingClientRect().width
|
||||
let maxWidth = Math.min(simulatorWidth / 2, 300)
|
||||
|
||||
while (scaleWidth > maxWidth) {
|
||||
scaleWidth /= 2.0
|
||||
}
|
||||
|
||||
while (scaleWidth < 100) {
|
||||
scaleWidth += one_mm
|
||||
}
|
||||
|
||||
let scaleMM = scaleWidth / one_mm
|
||||
|
||||
this.scale.plot(`M0,0 v10 h${scaleWidth / 2} v-5 v5 h${scaleWidth / 2} v-10`)
|
||||
|
||||
// round and strip trailing zeros, source: https://stackoverflow.com/a/53397618
|
||||
let mm = scaleMM.toFixed(8).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1')
|
||||
this.scaleLabel.text(`${mm} mm`)
|
||||
}, 100, {leading: true, trailing: true}
|
||||
),
|
||||
generateMarks() {
|
||||
this.commandList = Array()
|
||||
for (let i = 1; i < this.stitches.length; i++) {
|
||||
if (this.stitches[i].trim) {
|
||||
this.trimMarks[i] = new SliderMark("trim", "cut")
|
||||
this.commandList.push(i)
|
||||
} else if (this.stitches[i].stop) {
|
||||
this.stopMarks[i] = new SliderMark("stop", "pause")
|
||||
this.commandList.push(i)
|
||||
} else if (this.stitches[i].jump) {
|
||||
this.jumpMarks[i] = new SliderMark("jump", "frog")
|
||||
this.commandList.push(i)
|
||||
} else if (this.stitches[i].color_change) {
|
||||
this.colorChangeMarks[i] = new SliderMark("color-change", "exchange-alt")
|
||||
this.commandList.push(i)
|
||||
}
|
||||
}
|
||||
},
|
||||
generateColorSections() {
|
||||
var currentStitch = 0
|
||||
this.stitchPlan.color_blocks.forEach(color_block => {
|
||||
this.sliderColorSections.push([
|
||||
(currentStitch + 1) / this.numStitches * 100,
|
||||
(currentStitch + color_block.stitches.length) / this.numStitches * 100,
|
||||
{backgroundColor: color_block.color.visible_on_white.hex}
|
||||
])
|
||||
currentStitch += color_block.stitches.length
|
||||
})
|
||||
},
|
||||
generateMarker(color) {
|
||||
return this.svg.marker(3, 3, add => {
|
||||
let needlePenetrationPoint = add.circle(3).fill(color).hide()
|
||||
this.needlePenetrationPoints.push(needlePenetrationPoint)
|
||||
})
|
||||
},
|
||||
generateScale() {
|
||||
let svg = SVG().addTo(this.$refs.simulator)
|
||||
svg.node.classList.add("simulation-scale")
|
||||
this.scale = svg.path("M0,0").stroke({color: "black", width: "1px"}).fill("none")
|
||||
this.scaleLabel = svg.text("0 mm").move(0, 12)
|
||||
this.scaleLabel.node.classList.add("simulation-scale-label")
|
||||
},
|
||||
generateCursor() {
|
||||
this.cursor =
|
||||
this.svg.path("M0,0 v2.8 h1.2 v-2.8 h2.8 v-1.2 h-2.8 v-2.8 h-1.2 v2.8 h-2.8 v1.2 h2.8")
|
||||
.stroke({
|
||||
width: 0.1,
|
||||
color: '#FFFFFF',
|
||||
})
|
||||
.fill('#000000')
|
||||
this.cursor.node.classList.add("cursor")
|
||||
},
|
||||
generateRealisticPaths() {
|
||||
|
||||
// Create Realistic Filter
|
||||
this.filter = this.svg.defs().filter()
|
||||
|
||||
this.filter.attr({id: "realistic-stitch-filter", x: "-0.1", y: "-0.1", height: "1.2", width: "1.2", style: "color-interpolation-filters:sRGB"})
|
||||
this.filter.gaussianBlur({id: "gaussianBlur1", stdDeviation: "1.5", in: "SourceAlpha"})
|
||||
this.filter.componentTransfer(function (add) {
|
||||
add.funcR({ type: "identity" }),
|
||||
add.funcG({ type: "identity" }),
|
||||
add.funcB({ type: "identity", slope: "4.53" }),
|
||||
add.funcA({ type: "gamma", slope: "0.149", intercept: "0", amplitude: "3.13", offset: "-0.33" })
|
||||
}).attr({id: "componentTransfer1", in: "gaussianBlur1"})
|
||||
this.filter.composite({id: "composite1", in: "componentTransfer1", in2: "SourceAlpha", operator: "in"})
|
||||
this.filter.gaussianBlur({id: "gaussianBlur2", in: "composite1", stdDeviation: 0.09})
|
||||
this.filter.morphology({id: "morphology1", in: "gaussianBlur2", operator: "dilate", radius: 0.1})
|
||||
this.filter.specularLighting({id: "specularLighting1", in: "morphology1", specularConstant: 0.709, surfaceScale: 30}).pointLight({z: 10})
|
||||
this.filter.gaussianBlur({id: "gaussianBlur3", in: "specularLighting1", stdDeviation: 0.04})
|
||||
this.filter.composite({id: "composite2", in: "gaussianBlur3", in2: "SourceGraphic", operator: "arithmetic", k2: 1, k3: 1, k1: 0, k4: 0})
|
||||
this.filter.composite({in: "composite2", in2: "SourceAlpha", operator: "in"})
|
||||
|
||||
// Create realistic paths in it's own group and move it behind the cursor
|
||||
this.realisticPreview = this.svg.group({id: 'realistic'}).backward()
|
||||
|
||||
this.stitchPlan.color_blocks.forEach(color_block => {
|
||||
let color = `${color_block.color.visible_on_white.hex}`
|
||||
let realistic_path_attrs = {fill: color, stroke: "none", filter: this.filter}
|
||||
|
||||
let stitching = false
|
||||
let prevStitch = null
|
||||
color_block.stitches.forEach(stitch => {
|
||||
|
||||
let realisticPath = null
|
||||
if (stitching && prevStitch) {
|
||||
|
||||
// Position
|
||||
let stitch_center = []
|
||||
stitch_center.x = (prevStitch.x + stitch.x) / 2.0
|
||||
stitch_center.y = (prevStitch.y + stitch.y) / 2.0
|
||||
|
||||
// Angle
|
||||
var stitch_angle = Math.atan2(stitch.y - prevStitch.y, stitch.x - prevStitch.x) * (180 / Math.PI)
|
||||
|
||||
// Length
|
||||
let path_length = Math.hypot(stitch.x - prevStitch.x, stitch.y - prevStitch.y)
|
||||
|
||||
var path = `M0,0 c 0.4,0,0.4,0.3,0.4,0.6 c 0,0.3,-0.1,0.6,-0.4,0.6 v 0.2,-0.2 h -${path_length} c -0.4,0,-0.4,-0.3,-0.4,-0.6 c 0,-0.3,0.1,-0.6,0.4,-0.6 v -0.2,0.2 z`
|
||||
path = svgpath(path).rotate(stitch_angle).toString()
|
||||
|
||||
realisticPath = this.realisticPreview.path(path).attr(realistic_path_attrs).center(stitch_center.x, stitch_center.y).hide()
|
||||
|
||||
} else {
|
||||
realisticPath = this.realisticPreview.rect(0, 1).attr(realistic_path_attrs).center(stitch.x, stitch.y).hide()
|
||||
}
|
||||
|
||||
this.realisticPaths.push(realisticPath)
|
||||
|
||||
if (stitch.trim || stitch.color_change) {
|
||||
stitching = false
|
||||
} else if (!stitch.jump) {
|
||||
stitching = true
|
||||
}
|
||||
prevStitch = stitch
|
||||
})
|
||||
})
|
||||
},
|
||||
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: 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 () {
|
||||
let [minx, miny, maxx, maxy] = this.stitchPlan.bounding_box
|
||||
let designWidth = maxx - minx
|
||||
let designHeight = maxy - miny
|
||||
this.svg.viewbox(0, 0, designWidth, designHeight);
|
||||
this.resizeCursor()
|
||||
},
|
||||
zoomPage () {
|
||||
this.svg.viewbox(this.page_specs.bbox.x, this.page_specs.bbox.y - 50, this.page_specs.bbox.width + 100, this.page_specs.bbox.height + 100)
|
||||
this.resizeCursor()
|
||||
},
|
||||
close () {
|
||||
window.close()
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
// non-reactive properties
|
||||
this.targetFPS = 30
|
||||
this.targetFramePeriod = 1000.0 / this.targetFPS
|
||||
this.renderedStitch = 0
|
||||
this.lastFrameStart = null
|
||||
this.stitchPaths = [null] // 1-indexed to match up with stitch number display
|
||||
this.realisticPaths = [null]
|
||||
this.stitches = [null]
|
||||
this.svg = null
|
||||
this.simulation = null
|
||||
this.realisticPreview = null
|
||||
this.timer = null
|
||||
this.sliderColorSections = []
|
||||
this.trimMarks = {}
|
||||
this.stopMarks = {}
|
||||
this.colorChangeMarks = {}
|
||||
this.jumpMarks = {}
|
||||
this.needlePenetrationPoints = []
|
||||
this.cursor = null
|
||||
this.page_specs = {}
|
||||
},
|
||||
mounted: function () {
|
||||
this.svg = SVG().addTo(this.$refs.simulator).size('100%', '100%').panZoom({zoomMin: 0.1})
|
||||
this.svg.node.classList.add('simulation')
|
||||
this.simulation = this.svg.group({id: 'line'})
|
||||
|
||||
this.loading = true
|
||||
|
||||
inkStitch.get('stitch_plan').then(response => {
|
||||
this.stitchPlan = response.data
|
||||
let [minx, miny, maxx, maxy] = this.stitchPlan.bounding_box
|
||||
let width = maxx - minx
|
||||
let height = maxy - miny
|
||||
this.svg.viewbox(0, 0, width, height);
|
||||
|
||||
this.stitchPlan.color_blocks.forEach(color_block => {
|
||||
let color = `${color_block.color.visible_on_white.hex}`
|
||||
let path_attrs = {fill: "none", stroke: color, "stroke-width": 0.3}
|
||||
let marker = this.generateMarker(color)
|
||||
|
||||
let stitching = false
|
||||
let prevStitch = null
|
||||
color_block.stitches.forEach(stitch => {
|
||||
stitch.x -= minx
|
||||
stitch.y -= miny
|
||||
|
||||
let path = null
|
||||
if (stitching && prevStitch) {
|
||||
path = this.simulation.path(`M${prevStitch.x},${prevStitch.y} ${stitch.x},${stitch.y}`).attr(path_attrs).hide()
|
||||
} else {
|
||||
path = this.simulation.path(`M${stitch.x},${stitch.y} ${stitch.x},${stitch.y}`).attr(path_attrs).hide()
|
||||
}
|
||||
path.marker('end', marker)
|
||||
this.stitchPaths.push(path)
|
||||
this.stitches.push(stitch)
|
||||
|
||||
if (stitch.trim || stitch.color_change) {
|
||||
stitching = false
|
||||
} else if (!stitch.jump) {
|
||||
stitching = true
|
||||
}
|
||||
|
||||
prevStitch = stitch
|
||||
})
|
||||
})
|
||||
|
||||
this.numStitches = this.stitches.length - 1
|
||||
this.generateMarks()
|
||||
this.generateColorSections()
|
||||
this.generateScale()
|
||||
this.generateCursor()
|
||||
this.resizeCursor()
|
||||
|
||||
this.loading = false
|
||||
|
||||
// v-on:keydown doesn't seem to work, maybe an Electron issue?
|
||||
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)
|
||||
|
||||
inkStitch.get('page_specs').then(response => {
|
||||
this.page_specs = response.data
|
||||
this.generatePage()
|
||||
})
|
||||
|
||||
this.start()
|
||||
}).catch(error => {
|
||||
this.loading = false
|
||||
if (error.response) {
|
||||
// Stitch plan generation had an error. Show it to the user.
|
||||
this.error_message = error.response.data.error_message
|
||||
} else if (error.request) {
|
||||
// We sent the request and didn't get a response.
|
||||
this.error_message = "Stitch plan generation failed."
|
||||
} else {
|
||||
// Something weird happened in axios.
|
||||
this.error_message = error.message
|
||||
}
|
||||
this.error = true
|
||||
})
|
||||
}
|
||||
}
|
Przed Szerokość: | Wysokość: | Rozmiar: 60 KiB |
|
@ -1,307 +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.
|
||||
*
|
||||
*/
|
||||
* {
|
||||
padding: 1px;
|
||||
margin: 1px;
|
||||
}
|
||||
.loading-icon {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
color: rgb(0, 51, 153);
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.loading {
|
||||
border-radius: 1rem;
|
||||
border: 3px solid rgb(0, 51, 153);
|
||||
background-color: rgba(0, 51, 153, 0.1);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.controls button {
|
||||
color: rgb(0, 51, 153);
|
||||
align-items: flex-start;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
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;
|
||||
border-style: outset;
|
||||
border-color: buttonface;
|
||||
border-image: initial;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.fa-spin-fast {
|
||||
animation: fa-spin 0.4s infinite linear;
|
||||
}
|
||||
|
||||
.fa-button {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.fa-fast {
|
||||
transform: skew(-15deg, -15deg) rotate(15deg) scale(1.25, 0.90);
|
||||
}
|
||||
|
||||
.fa-motion-lines {
|
||||
transform: scale(1.0, 1.6) translate(0, -18%) skew(-15deg, -15deg) rotate(15deg);
|
||||
}
|
||||
|
||||
.fa-thin-line {
|
||||
transform: scale(1.4, 0.4);
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.panel > * {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.panel fieldset {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 2px solid rgb(0, 51, 153);
|
||||
position: relative;
|
||||
padding: 0 5px;
|
||||
font-size: 90%;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.window-controls {
|
||||
position: absolute;
|
||||
top: -0.2rem;
|
||||
right: -2px;
|
||||
font-size: 0.8rem;
|
||||
color: white;
|
||||
background: rgb(0, 51, 153);
|
||||
transform: scale(1, 0.8) translate(0, -100%);
|
||||
}
|
||||
|
||||
.window-controls > div {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.control-info {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow-y: auto;
|
||||
transform: translate(-100%, -100%);
|
||||
padding: 0 1rem 1rem;
|
||||
background: white;
|
||||
color: black;
|
||||
border: 1px solid rgb(0, 51, 153);
|
||||
border-radius: 5px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.control-info h1 {
|
||||
background: rgb(0, 51, 153);
|
||||
color: white;
|
||||
margin: 0 -1rem 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.control-info div {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.control-info > div {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.control-info div:first-child p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.control-info p {
|
||||
display: table-cell;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
fieldset button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
fieldset.command span.current-command {
|
||||
display: block;
|
||||
width: 18rem;
|
||||
margin: 0 auto;
|
||||
font-family: sans-serif;
|
||||
font-size: 2rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
fieldset.show-commands {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
fieldset.show-commands legend {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
fieldset.show-commands span {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
fieldset.show-commands span:first-of-type {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
button.pressed {
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
.vue-slider {
|
||||
height: 10px !important;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
margin: 25px 5px;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.slider-container > * {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.slider-box {
|
||||
width: calc(100% - 11rem);
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.slider-container :deep(.vue-slider-mark-step) {
|
||||
width: 2px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
background: #545454;
|
||||
transform: translate(1px, -25%);
|
||||
box-shadow: inset 1px 0 1px #ffffffbd, inset -1px 0 1px black;
|
||||
}
|
||||
|
||||
.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 :deep(.vue-slider-rail) {
|
||||
border: 1px solid #dedede;
|
||||
}
|
||||
|
||||
.slider-container :deep(.vue-slider-process) {
|
||||
border-radius: 0;
|
||||
box-shadow: inset 0px 2px 2px #ffffff80, inset 0px -2px 2px #0000002e;
|
||||
}
|
||||
|
||||
.slider-container :deep(.vue-slider-process:first-child) {
|
||||
border-top-left-radius: 15px;
|
||||
border-bottom-left-radius: 15px;
|
||||
}
|
||||
|
||||
.slider-container :deep(.vue-slider-process:nth-last-child(3)) {
|
||||
border-top-right-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
}
|
||||
|
||||
.vue-slider-mark-label {
|
||||
font-size: 1.4rem;
|
||||
color: #545454;
|
||||
}
|
||||
|
||||
.vue-slider-mark-label svg path {
|
||||
stroke: white;
|
||||
stroke-width: 10px;
|
||||
}
|
||||
|
||||
.slider-label-trim {
|
||||
transform: scale(1, 0.75) translate(-50%, -9px) !important;;
|
||||
}
|
||||
|
||||
.slider-label-stop {
|
||||
font-size: 1.2rem;
|
||||
transform: translate(-50%, 12px) !important;;
|
||||
}
|
||||
|
||||
.slider-label-jump {
|
||||
transform: translate(-50%, -50px) !important;;
|
||||
}
|
||||
|
||||
.slider-label-color-change {
|
||||
transform: translate(-50%, 10px) !important;;
|
||||
}
|
||||
|
||||
.current-stitch-input {
|
||||
width: 4rem;
|
||||
float: right;
|
||||
font-size: 1rem;
|
||||
border-style: inset;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
.simulator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 95vh;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.current-command {
|
||||
color: rgb(0, 51, 153);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 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 :deep(svg.simulation) {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
order: -2;
|
||||
margin-bottom: -48px;
|
||||
}
|
||||
|
||||
div.simulator :deep(svg.simulation-scale) {
|
||||
height: 50px;
|
||||
order: -1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
div.simulator :deep(.simulation-scale-label) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.simulator :deep(.cursor) {
|
||||
/* not sure what to add here to make it more visible */
|
||||
}
|
|
@ -1,12 +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.
|
||||
|
||||
-->
|
||||
|
||||
<template>
|
||||
<h1>Oops, it looks like the page you're looking for doesn't exist.</h1>
|
||||
</template>
|
|
@ -1,327 +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.
|
||||
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div ref="simulator" class="simulator vld-parent">
|
||||
<fieldset>
|
||||
<div class="window-controls">
|
||||
<div ref="controlInfoButton" class="control-info-button" v-on:click="toggleInfo">
|
||||
<font-awesome-icon icon="info"/>
|
||||
<Transition name="collapse">
|
||||
<div class="control-info" v-show="infoExpanded" v-bind:style="{'max-height': infoMaxHeight + 'px'}">
|
||||
<h1>
|
||||
<font-awesome-icon icon="info" class="info-icon"/>
|
||||
<translate>Simulator Shortcut Keys</translate>
|
||||
</h1>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<translate>Button</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Function</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Shortcut Key</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="pause" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Pause</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Space</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="play" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Play</translate>
|
||||
</p>
|
||||
<p>P</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="angle-double-left" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Play backward</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="name for left arrow keyboard key">← Arrow left</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="angle-double-right" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Play forward</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="name for right arrow keyboard key">→ Arrow right</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="shoe-prints" class="fa-button fa-flip-horizontal"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="description of keyboard shortcut that moves one stitch backward in simulator">
|
||||
One step backward
|
||||
</translate>
|
||||
</p>
|
||||
<p>-
|
||||
<translate translate-comment="name for this keyboard key: -">Minus</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="shoe-prints" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="description of keyboard shortcut that moves one stitch forward in simulator">
|
||||
One step forward
|
||||
</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="name for this keyboard key: +">+ Plus</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="step-backward" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Jump to previous command</translate>
|
||||
</p>
|
||||
<p><translate translate-comment="name for page down keyboard key">Page down (PgDn)</translate></p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="step-forward" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Jump to next command</translate>
|
||||
</p>
|
||||
<p><translate translate-comment="name for page up keyboard key">Page up (PgUp)</translate></p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="hippo" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Slow down</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="name for down arrow keyboard key">↓ Arrow down</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="horse" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Speed up</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate translate-comment="name for up arrow keyboard key">↑ Arrow up</translate>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="search-minus" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Zoom page</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate>[ Left square bracket</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<font-awesome-icon icon="search-plus" class="fa-button"/>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Zoom design</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate>] Right square bracket</translate>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<div class="toggle-controls" v-on:click="toggleControls">
|
||||
<font-awesome-icon v-if="controlsExpanded" icon="minus"/>
|
||||
<font-awesome-icon v-else icon="plus"/>
|
||||
</div>
|
||||
</div>
|
||||
<Transition name="collapse">
|
||||
<div class="panel" v-show="controlsExpanded">
|
||||
<fieldset class="controls">
|
||||
<legend>
|
||||
<translate>Controls</translate>
|
||||
</legend>
|
||||
<button v-on:click="stop" :class="{pressed: paused}" :title="$gettext('Pause (space)')">
|
||||
<font-awesome-icon icon="pause" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
<button v-on:click="start" :class="{pressed: animating}" :title="$gettext('Play (arrow left | arrow right)')">
|
||||
<font-awesome-icon icon="play" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
<button v-on:click="animationReverse" :class="{pressed: reverse}" :title="$gettext('Play backward (arrow left)')">
|
||||
<font-awesome-icon icon="angle-double-left" size="2x" class="fa-button" :mask="['fas', 'stop']"/>
|
||||
</button>
|
||||
<button v-on:click="animationForward" :class="{pressed: forward}" :title="$gettext('Play forward (arrow right)')">
|
||||
<font-awesome-icon icon="angle-double-right" size="2x" class="fa-button" :mask="['fas', 'stop']"/>
|
||||
</button>
|
||||
<button v-on:click="animationBackwardOneStitch" :title="$gettext('One step backward (-)')">
|
||||
<font-awesome-icon icon="shoe-prints" size="2x" class="fa-button fa-flip-horizontal"/>
|
||||
</button>
|
||||
<button v-on:click="animationForwardOneStitch" :title="$gettext('One step forward (+)')">
|
||||
<font-awesome-icon icon="shoe-prints" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
<button v-on:click="animationPreviousCommand" :title="$gettext('Jump to previous command (Page down)')">
|
||||
<font-awesome-icon icon="step-backward" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
<button v-on:click="animationNextCommand" :title="$gettext('Jump to next command (Page up)')">
|
||||
<font-awesome-icon icon="step-forward" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
</fieldset>
|
||||
<fieldset class="speed">
|
||||
<legend>
|
||||
<translate :translate-params="{ speed: speed }" :translate-n="speed" translate-plural="Speed: %{speed} stitches/sec">Speed: %{speed} stitch/sec</translate>
|
||||
</legend>
|
||||
<button v-on:click="animationSlowDown" :title="$gettext('Slow down (arrow down)')">
|
||||
<font-awesome-icon icon="hippo" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
<button v-on:click="animationSpeedUp" :title="$gettext('Speed up (arrow up)')">
|
||||
<font-awesome-icon icon="align-right" class="fa-motion-lines"/>
|
||||
<font-awesome-icon icon="horse" size="2x" class="fa-button fa-fast"/>
|
||||
</button>
|
||||
</fieldset>
|
||||
<fieldset class="view">
|
||||
<legend>
|
||||
<translate>View</translate>
|
||||
</legend>
|
||||
<button v-on:click="zoomPage" :title="$gettext('Zoom page ([)')">
|
||||
<font-awesome-icon icon="search-minus" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
<button v-on:click="zoomDesign" :title="$gettext('Zoom design (])')">
|
||||
<font-awesome-icon icon="search-plus" size="2x" class="fa-button"/>
|
||||
</button>
|
||||
</fieldset>
|
||||
<fieldset class="command">
|
||||
<legend>
|
||||
<translate>Command</translate>
|
||||
</legend>
|
||||
<span class="current-command">{{currentCommand}}</span>
|
||||
</fieldset>
|
||||
<fieldset class="show-commands">
|
||||
<legend>
|
||||
<translate>Show</translate>
|
||||
</legend>
|
||||
<span>
|
||||
<input id="trim-checkbox" type="checkbox" v-model="showTrims"/>
|
||||
<label for="trim-checkbox"><font-awesome-icon icon="cut"/> <translate>trims</translate></label>
|
||||
<br/>
|
||||
<input id="jump-checkbox" type="checkbox" v-model="showJumps"/>
|
||||
<label for="jump-checkbox"><font-awesome-icon icon="frog"/> <translate>jumps</translate></label>
|
||||
</span>
|
||||
<span>
|
||||
<input id="color-change-checkbox" type="checkbox" v-model="showColorChanges"/>
|
||||
<label for="color-change-checkbox"><font-awesome-icon icon="exchange-alt"/> <translate>color changes</translate></label>
|
||||
<br/>
|
||||
<input id="stop-checkbox" type="checkbox" v-model="showStops"/>
|
||||
<label for="stop-checkbox"><font-awesome-icon icon="pause"/> <translate>stops</translate></label>
|
||||
</span>
|
||||
<span class="npp">
|
||||
<input id="npp-checkbox" type="checkbox" v-model="showNeedlePenetrationPoints"/>
|
||||
<label for="npp-checkbox">
|
||||
<font-awesome-layers>
|
||||
<font-awesome-icon icon="circle" transform="shrink-9"/>
|
||||
<font-awesome-icon icon="minus" class="fa-thin-line"/>
|
||||
</font-awesome-layers>
|
||||
<span v-translate>needle points</span>
|
||||
</label>
|
||||
<br />
|
||||
<input id="render-jumps-checkbox" type="checkbox" v-model="renderJumps"/>
|
||||
<label for="render-jumps-checkbox"><font-awesome-icon icon="link"/><span v-translate>render jumps</span></label>
|
||||
</span>
|
||||
<span>
|
||||
<input id="realistic-checkbox" type="checkbox" v-model="showRealisticPreview"/>
|
||||
<label for="realistic-checkbox"><font-awesome-icon icon="eye"/> <span v-translate>realistic</span></label>
|
||||
<br/>
|
||||
<input id="cursor-checkbox" type="checkbox" v-model="showCursor"/>
|
||||
<label for="cursor-checkbox"><font-awesome-icon icon="plus"/> <span v-translate>cursor</span></label>
|
||||
</span>
|
||||
</fieldset>
|
||||
</div>
|
||||
</Transition>
|
||||
<div class="slider-container">
|
||||
<span>1</span>
|
||||
<span class="slider-box">
|
||||
<vue-slider v-model="currentStitchDisplay"
|
||||
@change="setCurrentStitch"
|
||||
:min="0"
|
||||
:max="numStitches"
|
||||
:duration="0"
|
||||
:marks="sliderMarks"
|
||||
:process="sliderProcess">
|
||||
<template v-slot:label="mark">
|
||||
<div :class="['vue-slider-mark-label', `slider-label-${mark.command}`, { active: mark.active }]">
|
||||
<font-awesome-icon :icon="mark.icon"/>
|
||||
</div>
|
||||
</template>
|
||||
</vue-slider>
|
||||
</span>
|
||||
<span>{{numStitches}}</span>
|
||||
<input ref="currentStitchInput"
|
||||
class="current-stitch-input"
|
||||
:value="currentStitchDisplay"
|
||||
@change="onCurrentStitchEntered"
|
||||
@focus="stop"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
<loading :active="loading" :is-full-page="false">
|
||||
<div class="loading">
|
||||
<div class="loading-icon">
|
||||
<font-awesome-icon icon="spinner" size="4x" pulse/>
|
||||
</div>
|
||||
<div class="loading-text">
|
||||
<translate>Rendering stitch-plan...</translate>
|
||||
</div>
|
||||
</div>
|
||||
</loading>
|
||||
<v-dialog v-model="error" width="auto">
|
||||
<v-card flat>
|
||||
<v-card-title class="pa-4">
|
||||
Error Generating Stitch Plan
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p style="white-space: pre-wrap;">{{ error_message }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn color="primary" variant="text" class="dialog-button" @click="close">Close</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="../assets/js/simulator.js"></script>
|
||||
|
||||
<style src="../assets/style/simulator.css" scoped></style>
|
|
@ -1,74 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ES6
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
|
||||
import { createGettext } from 'vue3-gettext'
|
||||
import translations from './assets/translations.json'
|
||||
import { selectLanguage } from '../lib/i18n.js'
|
||||
|
||||
import flaskserverport from '../lib/flaskserverport.json'
|
||||
|
||||
import { createVuetify, ThemeDefinition } from 'vuetify'
|
||||
import * as components from 'vuetify/components'
|
||||
import * as directives from 'vuetify/directives'
|
||||
import 'vuetify/styles'
|
||||
|
||||
import VueMousetrapPlugin from 'vue-mousetrap'
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
const inkStitchTheme = {
|
||||
dark: false,
|
||||
colors: {
|
||||
primary: '#003399',
|
||||
secondary: '#000000',
|
||||
accent: '#8c9eff',
|
||||
error: '#b71c1c',
|
||||
}
|
||||
}
|
||||
const vuetify = new createVuetify({
|
||||
components,
|
||||
directives,
|
||||
ssr: true,
|
||||
theme: {
|
||||
defaultTheme: 'inkStitchTheme',
|
||||
themes: {
|
||||
inkStitchTheme,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
library.add(fas)
|
||||
const app = createApp(App)
|
||||
app.component('font-awesome-icon', FontAwesomeIcon)
|
||||
app.component('font-awesome-layers', FontAwesomeLayers)
|
||||
|
||||
app.use(createGettext({
|
||||
defaultLanguage: selectLanguage(translations, theflaskport),
|
||||
translations: translations,
|
||||
silent: true,
|
||||
setGlobalProperties: true,
|
||||
}))
|
||||
app.use(VueMousetrapPlugin)
|
||||
app.use(vuetify)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
|
@ -1,31 +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.
|
||||
*
|
||||
*/
|
||||
import { createWebHashHistory, createRouter } from 'vue-router'
|
||||
const routes = [
|
||||
{
|
||||
path: '/simulator',
|
||||
name: 'simulator',
|
||||
component: () => import('../components/Simulator.vue')
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'NotFound',
|
||||
component: () => import('../components/NotFound.vue')
|
||||
},
|
||||
]
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
// Sets title for each routes
|
||||
const DEFAULT_TITLE = 'Ink/Stitch';
|
||||
|
||||
router.beforeEach((to) => {
|
||||
document.title = to.meta.title || DEFAULT_TITLE
|
||||
})
|
||||
export default router
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["webpack-env"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable", "ScriptHost"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
Przed Szerokość: | Wysokość: | Rozmiar: 8.0 KiB Po Szerokość: | Wysokość: | Rozmiar: 8.0 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 938 B Po Szerokość: | Wysokość: | Rozmiar: 938 B |
Przed Szerokość: | Wysokość: | Rozmiar: 1.5 KiB Po Szerokość: | Wysokość: | Rozmiar: 1.5 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 19 KiB Po Szerokość: | Wysokość: | Rozmiar: 19 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 2.0 KiB Po Szerokość: | Wysokość: | Rozmiar: 2.0 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 3.0 KiB Po Szerokość: | Wysokość: | Rozmiar: 3.0 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 38 KiB Po Szerokość: | Wysokość: | Rozmiar: 38 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 4.0 KiB Po Szerokość: | Wysokość: | Rozmiar: 4.0 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 6.0 KiB Po Szerokość: | Wysokość: | Rozmiar: 6.0 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 39 KiB Po Szerokość: | Wysokość: | Rozmiar: 39 KiB |
|
@ -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-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.debugger</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,18 +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">
|
||||
<array>
|
||||
<dict>
|
||||
<key>BundleHasStrictIdentifier</key>
|
||||
<false/>
|
||||
<key>BundleIsRelocatable</key>
|
||||
<false/>
|
||||
<key>BundleIsVersionChecked</key>
|
||||
<false/>
|
||||
<key>BundleOverwriteAction</key>
|
||||
<string>install</string>
|
||||
<key>RootRelativeBundlePath</key>
|
||||
<string>Contents/Frameworks/electron/inkstitch-gui.app</string>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
|
@ -264,7 +264,7 @@ class PrintPreviewServer(Thread):
|
|||
|
||||
self.host = "127.0.0.1"
|
||||
self.port = self.find_free_port()
|
||||
# exporting the port number for languages to work in electron vuejs part of inkstitch
|
||||
# exporting the port number for languages to work
|
||||
os.environ['FLASKPORT'] = str(self.port)
|
||||
|
||||
self.flask_server = make_server(self.host, self.port, self.app)
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
from .dialogs import confirm_dialog, info_dialog
|
||||
from .electron import open_url
|
||||
from .presets import PresetsPanel
|
||||
from .simulator import PreviewRenderer
|
||||
from .warnings import WarningPanel
|
||||
|
|
|
@ -1,91 +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.
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from ..utils import get_bundled_dir
|
||||
|
||||
app_process = None
|
||||
|
||||
|
||||
def open_url(url, port, pdf=False): # noqa: C901
|
||||
global app
|
||||
|
||||
if not pdf:
|
||||
url = f'{url}?port={port}'
|
||||
os.environ['FLASKPORT'] = str(port)
|
||||
|
||||
# this creates the .json for dev mode to get translations
|
||||
if getattr(sys, 'frozen', None) is None:
|
||||
dynamic_port = {
|
||||
"_comment1": "port should not be declared when commiting",
|
||||
"port": port,
|
||||
}
|
||||
port_object = json.dumps(dynamic_port, indent=1)
|
||||
with open(os.path.join("electron/src/lib/flaskserverport.json"), "w") as outfile:
|
||||
outfile.write(port_object)
|
||||
else:
|
||||
url = f'http://{url}:{port}/'
|
||||
|
||||
cwd = None
|
||||
searchstring = "http"
|
||||
|
||||
if getattr(sys, 'frozen', None) is not None:
|
||||
electron_path = os.path.join(get_bundled_dir("electron"), "inkstitch-gui")
|
||||
|
||||
if sys.platform == "darwin":
|
||||
electron_path = os.path.join(sys._MEIPASS, "electron", "inkstitch-gui.app", "Contents", "MacOS", "inkstitch-gui")
|
||||
command = ["open", "-W", "-a", electron_path, "--args", url]
|
||||
else:
|
||||
command = [electron_path, url]
|
||||
else:
|
||||
# if we're not running in a pyinstaller bundle, run electron directly
|
||||
command = ["yarn", "dev", url]
|
||||
cwd = get_bundled_dir("electron")
|
||||
|
||||
# Any output on stdout will crash inkscape.
|
||||
# In macos manual install the python env paths are incomplete
|
||||
# Adding the yarn path to the env paths fixes this issue
|
||||
if sys.platform == "darwin" and getattr(sys, 'frozen', None) is None:
|
||||
mac_dev_env = os.environ.copy()
|
||||
# these are paths installed by brew or macports
|
||||
yarn_path = "/opt/homebrew/bin:/usr/local/bin:/opt/local/bin:"
|
||||
if yarn_path in mac_dev_env["PATH"]:
|
||||
pass
|
||||
else:
|
||||
mac_dev_env["PATH"] = yarn_path + mac_dev_env["PATH"]
|
||||
# checking URL for flask server address for printToPDF
|
||||
if searchstring in url:
|
||||
with open(os.devnull, 'w') as null:
|
||||
subprocess.Popen(["yarn", "just-build"], cwd=cwd, stdout=null, env=mac_dev_env).wait()
|
||||
else:
|
||||
pass
|
||||
|
||||
with open(os.devnull, 'w') as null:
|
||||
return subprocess.Popen(command, cwd=cwd, stdout=null, env=mac_dev_env)
|
||||
else:
|
||||
if searchstring in url and getattr(sys, 'frozen', None) is None:
|
||||
with open(os.devnull, 'w') as null:
|
||||
subprocess.Popen(["yarn", "just-build"], cwd=cwd, stdout=null).wait()
|
||||
else:
|
||||
pass
|
||||
if sys.platform == "linux":
|
||||
# Pyinstaller fix for gnome document view not opening.
|
||||
lenv = dict(os.environ)
|
||||
lp_key = 'LD_LIBRARY_PATH'
|
||||
lp_orig = lenv.get(lp_key + '_ORIG')
|
||||
if lp_orig is not None:
|
||||
lenv[lp_key] = lp_orig # restore the original, unmodified value
|
||||
else:
|
||||
lenv.pop(lp_key, None)
|
||||
|
||||
with open(os.devnull, 'w') as null:
|
||||
return subprocess.Popen(command, cwd=cwd, stdout=null, env=lenv)
|
||||
else:
|
||||
with open(os.devnull, 'w') as null:
|
||||
return subprocess.Popen(command, cwd=cwd, stdout=null)
|