Compare commits

..

2 Commits

Author SHA1 Message Date
Jake Archibald
fd98d67b3e lol this was meant to be 10 seconds. 2018-12-07 14:26:45 +00:00
Jake Archibald
db1db8506e Move early exit for no-rotation. 2018-12-07 14:26:22 +00:00
25 changed files with 643 additions and 1126 deletions

View File

@@ -1,2 +0,0 @@
/index.html / 301
/* /index.html 301

View File

@@ -8,6 +8,6 @@
"libimagequant": "ImageOptim/libimagequant#2.12.1"
},
"devDependencies": {
"napa": "3.0.0"
"napa": "^3.0.0"
}
}

View File

@@ -8,6 +8,6 @@
"mozjpeg": "mozilla/mozjpeg#v3.3.1"
},
"devDependencies": {
"napa": "3.0.0"
"napa": "^3.0.0"
}
}

View File

@@ -1,6 +1,6 @@
# OptiPNG
- Source: <http://optipng.sourceforge.net/>
- Source: <https://sourceforge.net/project/optipng>
- Version: v0.7.7
## Dependencies

View File

@@ -16,7 +16,7 @@
"zlib": "emscripten-ports/zlib"
},
"dependencies": {
"napa": "3.0.0",
"napa": "^3.0.0",
"tar-dependency": "0.0.3"
}
}

View File

@@ -1,124 +0,0 @@
{
"name": "rotate",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
"dev": true
},
"assemblyscript": {
"version": "github:AssemblyScript/assemblyscript#54b02c287c4d42092e4b01cc5d11bce1a53e9fb2",
"from": "github:AssemblyScript/assemblyscript",
"dev": true,
"requires": {
"@protobufjs/utf8": "^1.1.0",
"binaryen": "63.0.0-nightly.20190107",
"glob": "^7.1.3",
"long": "^4.0.0"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"binaryen": {
"version": "63.0.0-nightly.20190107",
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-63.0.0-nightly.20190107.tgz",
"integrity": "sha512-vck6ZESU9q6DeEK9v/Fk2O8d2jBsSrNlia8jpmdLZv5eqmNWpkNli+QMRa9Ezfb4wi3c+NVMuxT3Ck0g9GYe8A==",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
}
}
}

View File

@@ -1,9 +0,0 @@
{
"name": "rotate",
"scripts": {
"build": "mv rotate.{as,ts} && asc rotate.ts -b rotate.wasm --validate -O3 --importMemory && mv rotate.{ts,as}"
},
"devDependencies": {
"assemblyscript": "github:AssemblyScript/assemblyscript"
}
}

View File

@@ -1,64 +0,0 @@
export function rotate(inputWidth: i32, inputHeight: i32, rotate: i32): void {
const bpp = 4;
let offset = inputWidth * inputHeight * bpp;
let i = 0;
// In the straight-copy case, d1 is x, d2 is y.
// x starts at 0 and increases.
// y starts at 0 and increases.
let d1Start = 0;
let d1Limit = inputWidth;
let d1Advance = 1;
let d1Multiplier = 1;
let d2Start = 0;
let d2Limit = inputHeight;
let d2Advance = 1;
let d2Multiplier = inputWidth;
if (rotate === 90) {
// d1 is y, d2 is x.
// y starts at its max value and decreases.
// x starts at 0 and increases.
d1Start = inputHeight - 1;
d1Limit = inputHeight;
d1Advance = -1;
d1Multiplier = inputWidth;
d2Start = 0;
d2Limit = inputWidth;
d2Advance = 1;
d2Multiplier = 1;
} else if (rotate === 180) {
// d1 is x, d2 is y.
// x starts at its max and decreases.
// y starts at its max and decreases.
d1Start = inputWidth - 1;
d1Limit = inputWidth;
d1Advance = -1;
d1Multiplier = 1;
d2Start = inputHeight - 1;
d2Limit = inputHeight;
d2Advance = -1;
d2Multiplier = inputWidth;
} else if (rotate === 270) {
// d1 is y, d2 is x.
// y starts at 0 and increases.
// x starts at its max and decreases.
d1Start = 0;
d1Limit = inputHeight;
d1Advance = 1;
d1Multiplier = inputWidth;
d2Start = inputWidth - 1;
d2Limit = inputWidth;
d2Advance = -1;
d2Multiplier = 1;
}
for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
let d2offset = d2 * d2Multiplier;
for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
let start = ((d1 * d1Multiplier) + d2offset);
store<u32>(offset + i * 4, load<u32>(start * 4));
i += 1;
}
}
}

Binary file not shown.

View File

@@ -1,6 +0,0 @@
{
"extends": "./node_modules/assemblyscript/std/assembly.json",
"include": [
"./**/*.ts"
]
}

View File

@@ -8,6 +8,6 @@
"libwebp": "webmproject/libwebp#v1.0.0"
},
"devDependencies": {
"napa": "3.0.0"
"napa": "^3.0.0"
}
}

View File

@@ -8,6 +8,6 @@
"libwebp": "webmproject/libwebp#v1.0.0"
},
"devDependencies": {
"napa": "3.0.0"
"napa": "^3.0.0"
}
}

View File

@@ -26,8 +26,7 @@ val encode(std::string img, int width, int height, WebPConfig config) {
throw std::runtime_error("Unexpected error");
}
// Only use use_argb if we really need it, as it's slower.
pic.use_argb = config.lossless || config.use_sharp_yuv || config.preprocessing > 0;
pic.use_argb = !!config.lossless;
pic.width = width;
pic.height = height;
pic.writer = WebPMemoryWrite;

File diff suppressed because one or more lines are too long

Binary file not shown.

1207
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "squoosh",
"version": "1.3.1",
"version": "1.2.2",
"license": "apache-2.0",
"scripts": {
"start": "webpack-dev-server --host 0.0.0.0 --hot",
@@ -15,55 +15,55 @@
}
},
"devDependencies": {
"@types/node": "10.12.18",
"@types/pretty-bytes": "5.1.0",
"@types/webassembly-js-api": "0.0.2",
"@webcomponents/custom-elements": "1.2.1",
"@webpack-cli/serve": "0.1.3",
"assets-webpack-plugin": "3.9.7",
"chokidar": "2.0.4",
"classnames": "2.2.6",
"clean-webpack-plugin": "1.0.1",
"comlink": "3.1.1",
"copy-webpack-plugin": "4.6.0",
"critters-webpack-plugin": "2.2.0",
"css-loader": "1.0.1",
"ejs": "2.6.1",
"exports-loader": "0.7.0",
"file-drop-element": "0.0.9",
"file-loader": "3.0.1",
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"idb-keyval": "3.1.0",
"linkstate": "1.1.1",
"loader-utils": "1.2.0",
"mini-css-extract-plugin": "0.5.0",
"minimatch": "3.0.4",
"node-sass": "4.11.0",
"optimize-css-assets-webpack-plugin": "5.0.1",
"pointer-tracker": "2.0.3",
"preact": "8.4.2",
"prerender-loader": "1.2.0",
"pretty-bytes": "5.1.0",
"progress-bar-webpack-plugin": "1.12.0",
"raw-loader": "1.0.0",
"sass-loader": "7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"source-map-loader": "0.2.4",
"style-loader": "0.23.1",
"terser-webpack-plugin": "1.2.1",
"ts-loader": "5.3.3",
"tslint": "5.12.1",
"tslint-config-airbnb": "5.11.1",
"tslint-config-semistandard": "7.0.0",
"tslint-react": "3.6.0",
"typed-css-modules": "0.3.7",
"typescript": "3.2.4",
"url-loader": "1.1.2",
"webpack": "4.28.0",
"webpack-bundle-analyzer": "3.0.3",
"webpack-cli": "3.2.1",
"webpack-dev-server": "3.1.14",
"worker-plugin": "3.0.0"
"@types/node": "^10.12.6",
"@types/pretty-bytes": "^5.1.0",
"@types/webassembly-js-api": "0.0.1",
"@webcomponents/custom-elements": "^1.2.1",
"@webpack-cli/serve": "^0.1.2",
"assets-webpack-plugin": "^3.9.7",
"chokidar": "^2.0.4",
"classnames": "^2.2.6",
"clean-webpack-plugin": "^1.0.0",
"comlink": "^3.0.3",
"copy-webpack-plugin": "^4.6.0",
"critters-webpack-plugin": "^2.0.1",
"css-loader": "^1.0.1",
"ejs": "^2.6.1",
"exports-loader": "^0.7.0",
"file-drop-element": "^0.0.9",
"file-loader": "^2.0.0",
"html-webpack-plugin": "^3.2.0",
"husky": "^1.1.4",
"idb-keyval": "^3.1.0",
"linkstate": "^1.1.1",
"loader-utils": "^1.1.0",
"mini-css-extract-plugin": "^0.4.4",
"minimatch": "^3.0.4",
"node-sass": "^4.11.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"pointer-tracker": "^2.0.3",
"preact": "^8.3.1",
"prerender-loader": "^1.2.0",
"pretty-bytes": "^5.1.0",
"progress-bar-webpack-plugin": "^1.11.0",
"raw-loader": "^0.5.1",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "^2.1.3",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.1",
"terser-webpack-plugin": "^1.1.0",
"ts-loader": "^5.3.0",
"tslint": "^5.11.0",
"tslint-config-airbnb": "^5.11.0",
"tslint-config-semistandard": "^7.0.0",
"tslint-react": "^3.6.0",
"typed-css-modules": "^0.3.7",
"typescript": "^3.1.6",
"url-loader": "^1.1.2",
"webpack": "^4.25.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10",
"worker-plugin": "^1.1.1"
}
}

View File

@@ -1,5 +0,0 @@
{
"extends": [
"config:base"
]
}

View File

@@ -3,10 +3,3 @@ export interface RotateOptions {
}
export const defaultOptions: RotateOptions = { rotate: 0 };
export interface RotateModuleInstance {
exports: {
memory: WebAssembly.Memory;
rotate(width: number, height: number, rotate: 0 | 90 | 180 | 270): void;
};
}

View File

@@ -1,30 +1,76 @@
import wasmUrl from '../../../codecs/rotate/rotate.wasm';
import { RotateOptions, RotateModuleInstance } from './processor-meta';
import { RotateOptions } from './processor-meta';
export async function rotate(
data: ImageData,
opts: RotateOptions,
): Promise<ImageData> {
const flipDimensions = opts.rotate % 180 !== 0;
// Number of wasm memory pages (á 64KiB) needed to store the image twice.
const bytesPerImage = data.width * data.height * 4;
const numPagesNeeded = Math.ceil(bytesPerImage * 2 / (64 * 1024));
const { instance } = (await (WebAssembly as any).instantiateStreaming(
fetch(wasmUrl),
{
env: {
memory: new WebAssembly.Memory({ initial: numPagesNeeded }),
},
},
)) as { instance: RotateModuleInstance };
const bpp = 4;
const view = new Uint8ClampedArray(instance.exports.memory.buffer);
view.set(data.data);
export function rotate(data: ImageData, opts: RotateOptions): ImageData {
const { rotate } = opts;
const flipDimensions = rotate % 180 !== 0;
const { width: inputWidth, height: inputHeight } = data;
const outputWidth = flipDimensions ? inputHeight : inputWidth;
const outputHeight = flipDimensions ? inputWidth : inputHeight;
const out = new ImageData(outputWidth, outputHeight);
let i = 0;
instance.exports.rotate(data.width, data.height, opts.rotate);
return new ImageData(
view.slice(bytesPerImage, bytesPerImage * 2),
flipDimensions ? data.height : data.width,
flipDimensions ? data.width : data.height,
);
// In the straight-copy case, d1 is x, d2 is y.
// x starts at 0 and increases.
// y starts at 0 and increases.
let d1Start = 0;
let d1Limit = inputWidth;
let d1Advance = 1;
let d1Multiplier = 1;
let d2Start = 0;
let d2Limit = inputHeight;
let d2Advance = 1;
let d2Multiplier = inputWidth;
if (rotate === 90) {
// d1 is y, d2 is x.
// y starts at its max value and decreases.
// x starts at 0 and increases.
d1Start = inputHeight - 1;
d1Limit = inputHeight;
d1Advance = -1;
d1Multiplier = inputWidth;
d2Start = 0;
d2Limit = inputWidth;
d2Advance = 1;
d2Multiplier = 1;
} else if (rotate === 180) {
// d1 is x, d2 is y.
// x starts at its max and decreases.
// y starts at its max and decreases.
d1Start = inputWidth - 1;
d1Limit = inputWidth;
d1Advance = -1;
d1Multiplier = 1;
d2Start = inputHeight - 1;
d2Limit = inputHeight;
d2Advance = -1;
d2Multiplier = inputWidth;
} else if (rotate === 270) {
// d1 is y, d2 is x.
// y starts at 0 and increases.
// x starts at its max and decreases.
d1Start = 0;
d1Limit = inputHeight;
d1Advance = 1;
d1Multiplier = inputWidth;
d2Start = inputWidth - 1;
d2Limit = inputWidth;
d2Advance = -1;
d2Multiplier = 1;
}
for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
// Iterate over channels:
const start = ((d1 * d1Multiplier) + (d2 * d2Multiplier)) * bpp;
for (let j = 0; j < bpp; j += 1) {
out.data[i] = data.data[start + j];
i += 1;
}
}
}
return out;
}

View File

@@ -9,8 +9,6 @@ import '../../lib/SnackBar';
import Intro from '../intro';
import '../custom-els/LoadingSpinner';
const ROUTE_EDITOR = '/editor';
const compressPromise = import(
/* webpackChunkName: "main-app" */
'../compress',
@@ -20,21 +18,15 @@ const offlinerPromise = import(
'../../lib/offliner',
);
function back() {
window.history.back();
}
interface Props {}
interface State {
file?: File | Fileish;
isEditorOpen: Boolean;
Compress?: typeof import('../compress').default;
}
export default class App extends Component<Props, State> {
state: State = {
isEditorOpen: false,
file: undefined,
Compress: undefined,
};
@@ -56,33 +48,22 @@ export default class App extends Component<Props, State> {
if (process.env.NODE_ENV === 'development') {
this.setState(window.STATE);
const oldCDU = this.componentDidUpdate;
this.componentDidUpdate = (props, state, prev) => {
if (oldCDU) oldCDU.call(this, props, state, prev);
this.componentDidUpdate = (props, state) => {
if (oldCDU) oldCDU.call(this, props, state);
window.STATE = this.state;
};
}
// Since iOS 10, Apple tries to prevent disabling pinch-zoom. This is great in theory, but
// really breaks things on Squoosh, as you can easily end up zooming the UI when you mean to
// zoom the image. Once you've done this, it's really difficult to undo. Anyway, this seems to
// prevent it.
document.body.addEventListener('gesturestart', (event) => {
event.preventDefault();
});
window.addEventListener('popstate', this.onPopState);
}
@bind
private onFileDrop({ file }: FileDropEvent) {
private onFileDrop(event: FileDropEvent) {
const { file } = event;
if (!file) return;
this.openEditor();
this.setState({ file });
}
@bind
private onIntroPickFile(file: File | Fileish) {
this.openEditor();
this.setState({ file });
}
@@ -93,25 +74,18 @@ export default class App extends Component<Props, State> {
}
@bind
private onPopState() {
this.setState({ isEditorOpen: location.pathname === ROUTE_EDITOR });
private onBack() {
this.setState({ file: undefined });
}
@bind
private openEditor() {
if (this.state.isEditorOpen) return;
history.pushState(null, '', ROUTE_EDITOR);
this.setState({ isEditorOpen: true });
}
render({}: Props, { file, isEditorOpen, Compress }: State) {
render({}: Props, { file, Compress }: State) {
return (
<div id="app" class={style.app}>
<file-drop accept="image/*" onfiledrop={this.onFileDrop} class={style.drop}>
{!isEditorOpen
{(!file)
? <Intro onFile={this.onIntroPickFile} showSnack={this.showSnack} />
: (Compress)
? <Compress file={file!} showSnack={this.showSnack} onBack={back} />
? <Compress file={file} showSnack={this.showSnack} onBack={this.onBack} />
: <loading-spinner class={style.appLoader}/>
}
<snack-bar ref={linkRef(this, 'snackbar')} />

View File

@@ -40,9 +40,7 @@ import Checkbox from '../checkbox';
import Expander from '../expander';
import Select from '../select';
const encoderOptionsComponentMap: {
[x: string]: (new (...args: any[]) => Component<any, any>) | undefined;
} = {
const encoderOptionsComponentMap = {
[identity.type]: undefined,
[optiPNG.type]: OptiPNGEncoderOptions,
[mozJPEG.type]: MozJpegEncoderOptions,

View File

@@ -43,7 +43,6 @@ $horizontalPadding: 15px;
.text-field {
background: #fff;
color: #000;
font: inherit;
border: none;
padding: 2px 0 2px 10px;

View File

@@ -160,7 +160,7 @@ function stateForNewSourceData(state: State, newSource: SourceImage): State {
for (const i of [0, 1]) {
// Ditch previous encodings
const downloadUrl = state.sides[i].downloadUrl;
if (downloadUrl) URL.revokeObjectURL(downloadUrl);
if (downloadUrl) URL.revokeObjectURL(downloadUrl!);
newState = cleanMerge(state, `sides.${i}`, {
preprocessed: undefined,
@@ -240,8 +240,6 @@ export default class Compress extends Component<Props, State> {
private readonly encodeCache = new ResultCache();
private readonly leftProcessor = new Processor();
private readonly rightProcessor = new Processor();
// For debouncing calls to updateImage for each side.
private readonly updateImageTimeoutIds: [number?, number?] = [undefined, undefined];
constructor(props: Props) {
super(props);
@@ -309,8 +307,10 @@ export default class Compress extends Component<Props, State> {
// The image only needs updated if the encoder/preprocessor settings have changed, or the
// source has changed.
if (sourceDataChanged || encoderChanged || preprocessorChanged) {
this.queueUpdateImage(i, {
this.updateImage(i, {
skipPreprocessing: !sourceDataChanged && !preprocessorChanged,
}).catch((err) => {
console.error(err);
});
}
}
@@ -319,14 +319,9 @@ export default class Compress extends Component<Props, State> {
private async onCopyToOtherClick(index: 0 | 1) {
const otherIndex = (index + 1) % 2;
const oldSettings = this.state.sides[otherIndex];
const newSettings = { ...this.state.sides[index] };
// Create a new object URL for the new settings. This avoids both sides sharing a URL, which
// means it can be safely revoked without impacting the other side.
if (newSettings.file) newSettings.downloadUrl = URL.createObjectURL(newSettings.file);
this.setState({
sides: cleanSet(this.state.sides, otherIndex, newSettings),
sides: cleanSet(this.state.sides, otherIndex, this.state.sides[index]),
});
const result = await this.props.showSnack('Settings copied across', {
@@ -457,27 +452,6 @@ export default class Compress extends Component<Props, State> {
}
}
/**
* Debounce the heavy lifting of updateImage.
* Otherwise, the thrashing causes jank, and sometimes crashes iOS Safari.
*/
private queueUpdateImage(index: number, options: UpdateImageOptions = {}): void {
// Call updateImage after this delay, unless queueUpdateImage is called again, in which case the
// timeout is reset.
const delay = 100;
clearTimeout(this.updateImageTimeoutIds[index]);
this.updateImageTimeoutIds[index] = self.setTimeout(
() => {
this.updateImage(index, options).catch((err) => {
console.error(err);
});
},
delay,
);
}
private async updateImage(index: number, options: UpdateImageOptions = {}): Promise<void> {
const {
skipPreprocessing = false,
@@ -576,9 +550,9 @@ export default class Compress extends Component<Props, State> {
mobileView={mobileView}
preprocessorState={side.latestSettings.preprocessorState}
encoderState={side.latestSettings.encoderState}
onEncoderTypeChange={this.onEncoderTypeChange.bind(this, index as 0|1)}
onEncoderOptionsChange={this.onEncoderOptionsChange.bind(this, index as 0|1)}
onPreprocessorOptionsChange={this.onPreprocessorOptionsChange.bind(this, index as 0|1)}
onEncoderTypeChange={this.onEncoderTypeChange.bind(this, index)}
onEncoderOptionsChange={this.onEncoderOptionsChange.bind(this, index)}
onPreprocessorOptionsChange={this.onPreprocessorOptionsChange.bind(this, index)}
/>
));
@@ -592,7 +566,7 @@ export default class Compress extends Component<Props, State> {
source={source}
loading={loading || side.loading}
copyDirection={copyDirections[index]}
onCopyToOtherClick={this.onCopyToOtherClick.bind(this, index as 0|1)}
onCopyToOtherClick={this.onCopyToOtherClick.bind(this, index)}
buttonPosition={mobileView ? 'stack-right' : buttonPositions[index]}
>
{!mobileView ? null : [

View File

@@ -251,11 +251,6 @@ module.exports = async function (_, env) {
filename: '_headers',
}),
isProd && new AssetTemplatePlugin({
template: path.join(__dirname, '_redirects.ejs'),
filename: '_redirects',
}),
new ScriptExtHtmlPlugin({
inline: ['first']
}),