Merge upstream

This commit is contained in:
Jason Miller
2020-12-10 10:24:56 -05:00
parent a2fb7a38cd
commit d1d181fccd
26 changed files with 358 additions and 29 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ node_modules
*.css.d.ts *.css.d.ts
build build
*.o *.o
.DS_Store
# Auto-generated by lib/feature-plugin.js # Auto-generated by lib/feature-plugin.js
src/features-worker/index.ts src/features-worker/index.ts

BIN
cli/.DS_Store vendored

Binary file not shown.

1
cli/.gitignore vendored
View File

@@ -1,2 +1,3 @@
node_modules node_modules
build build
.DS_Store

View File

@@ -47,7 +47,7 @@ The default values for each `config` option can be found in the [`codecs.js`][co
Squoosh CLI has an _experimental_ auto optimizer that compresses an image as much as possible, trying to hit a specific [Butteraugli] target value. The higher the Butteraugli target value, the more artifacts can be introduced. Squoosh CLI has an _experimental_ auto optimizer that compresses an image as much as possible, trying to hit a specific [Butteraugli] target value. The higher the Butteraugli target value, the more artifacts can be introduced.
You can make use of the auto optimizer buy using “auto” as the config object. You can make use of the auto optimizer by using “auto” as the config object.
``` ```
$ npx @squoosh/cli --wp2 auto test.png $ npx @squoosh/cli --wp2 auto test.png

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

2
cli/package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.5.0", "version": "0.6.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.5.0", "version": "0.6.0",
"description": "A CLI for Squoosh", "description": "A CLI for Squoosh",
"public": true, "public": true,
"bin": { "bin": {

View File

@@ -4,6 +4,7 @@ import { isMainThread } from 'worker_threads';
import { cpus } from 'os'; import { cpus } from 'os';
import { extname, join, basename } from 'path'; import { extname, join, basename } from 'path';
import { promises as fsp } from 'fs'; import { promises as fsp } from 'fs';
import { resolve as resolvePath } from 'path';
import { version } from 'json:../package.json'; import { version } from 'json:../package.json';
import ora from 'ora'; import ora from 'ora';
import kleur from 'kleur'; import kleur from 'kleur';
@@ -182,7 +183,32 @@ function progressTracker(results) {
return tracker; return tracker;
} }
async function checkInputFilesValid(files) {
const validFiles = [];
for (const file of files) {
try {
await fsp.stat(file);
} catch (err) {
if (err.code === 'ENOENT') {
console.warn(
`Warning: Input file does not exist: ${resolvePath(file)}`,
);
continue;
} else {
throw err;
}
}
validFiles.push(file);
}
return validFiles;
}
async function processFiles(files) { async function processFiles(files) {
files = await checkInputFilesValid(files);
const parallelism = cpus().length; const parallelism = cpus().length;
const results = new Map(); const results = new Map();

File diff suppressed because one or more lines are too long

View File

@@ -112,7 +112,9 @@ export default async function ({ watch }) {
plugins: [ plugins: [
{ resolveFileUrl, resolveImportMeta }, { resolveFileUrl, resolveImportMeta },
OMT({ loader: await omtLoaderPromise }), OMT({ loader: await omtLoaderPromise }),
serviceWorkerPlugin({ output: 'static/sw.js' }), serviceWorkerPlugin({
output: 'static/serviceworker.js',
}),
...commonPlugins(), ...commonPlugins(),
commonjs(), commonjs(),
resolve(), resolve(),

View File

@@ -13,11 +13,11 @@ import {
encoderMap, encoderMap,
} from '../../feature-meta'; } from '../../feature-meta';
import Expander from './Expander'; import Expander from './Expander';
import Checkbox from './Checkbox';
import Toggle from './Toggle'; import Toggle from './Toggle';
import Select from './Select'; import Select from './Select';
import { Options as QuantOptionsComponent } from 'features/processors/quantize/client'; import { Options as QuantOptionsComponent } from 'features/processors/quantize/client';
import { Options as ResizeOptionsComponent } from 'features/processors/resize/client'; import { Options as ResizeOptionsComponent } from 'features/processors/resize/client';
import { CLIIcon, SwapIcon } from 'client/lazy-app/icons';
interface Props { interface Props {
index: 0 | 1; index: 0 | 1;
@@ -28,6 +28,8 @@ interface Props {
onEncoderTypeChange(index: 0 | 1, newType: OutputType): void; onEncoderTypeChange(index: 0 | 1, newType: OutputType): void;
onEncoderOptionsChange(index: 0 | 1, newOptions: EncoderOptions): void; onEncoderOptionsChange(index: 0 | 1, newOptions: EncoderOptions): void;
onProcessorOptionsChange(index: 0 | 1, newOptions: ProcessorState): void; onProcessorOptionsChange(index: 0 | 1, newOptions: ProcessorState): void;
onCopyToOtherSideClick(index: 0 | 1): void;
onCopyCliClick(index: 0 | 1): void;
} }
interface State { interface State {
@@ -106,6 +108,14 @@ export default class Options extends Component<Props, State> {
this.props.onEncoderOptionsChange(this.props.index, newOptions); this.props.onEncoderOptionsChange(this.props.index, newOptions);
}; };
private onCopyCliClick = () => {
this.props.onCopyCliClick(this.props.index);
};
private onCopyToOtherSideClick = () => {
this.props.onCopyToOtherSideClick(this.props.index);
};
render( render(
{ source, encoderState, processorState }: Props, { source, encoderState, processorState }: Props,
{ supportedEncoderMap }: State, { supportedEncoderMap }: State,
@@ -125,7 +135,25 @@ export default class Options extends Component<Props, State> {
<Expander> <Expander>
{!encoderState ? null : ( {!encoderState ? null : (
<div> <div>
<h3 class={style.optionsTitle}>Edit</h3> <h3 class={style.optionsTitle}>
<div class={style.titleAndButtons}>
Edit
<button
class={style.cliButton}
title="Copy npx command"
onClick={this.onCopyCliClick}
>
<CLIIcon />
</button>
<button
class={style.copyOverButton}
title="Copy settings to other side"
onClick={this.onCopyToOtherSideClick}
>
<SwapIcon />
</button>
</div>
</h3>
<label class={style.sectionEnabler}> <label class={style.sectionEnabler}>
Resize Resize
<Toggle <Toggle

View File

@@ -81,3 +81,37 @@
box-sizing: border-box; box-sizing: border-box;
border-radius: 4px; border-radius: 4px;
} }
.title-and-buttons {
grid-template-columns: 1fr;
grid-auto-columns: max-content;
grid-auto-flow: column;
display: grid;
gap: 0.8rem;
}
.title-button {
composes: unbutton from global;
svg {
--size: 20px;
display: block;
width: var(--size);
height: var(--size);
}
}
.cli-button {
composes: title-button;
svg {
stroke: var(--header-text-color);
}
}
.copy-over-button {
composes: title-button;
svg {
fill: var(--header-text-color);
}
}

View File

@@ -337,7 +337,7 @@ export default class Output extends Component<Props, State> {
</div> </div>
<div class={style.controls} hidden={hidden}> <div class={style.controls} hidden={hidden}>
<div class={style.zoomControls}> <div class={style.buttonGroup}>
<button class={style.button} onClick={this.zoomOut}> <button class={style.button} onClick={this.zoomOut}>
<RemoveIcon /> <RemoveIcon />
</button> </button>

View File

@@ -66,7 +66,7 @@
} }
} }
.zoom-controls { .button-group {
display: flex; display: flex;
position: relative; position: relative;
z-index: 100; z-index: 100;

View File

@@ -411,7 +411,7 @@ export default class Compress extends Component<Props, State> {
this.queueUpdateImage(); this.queueUpdateImage();
} }
private async onCopyToOtherClick(index: 0 | 1) { private onCopyToOtherClick = async (index: 0 | 1) => {
const otherIndex = index ? 0 : 1; const otherIndex = index ? 0 : 1;
const oldSettings = this.state.sides[otherIndex]; const oldSettings = this.state.sides[otherIndex];
const newSettings = { ...this.state.sides[index] }; const newSettings = { ...this.state.sides[index] };
@@ -436,7 +436,7 @@ export default class Compress extends Component<Props, State> {
this.setState({ this.setState({
sides: cleanSet(this.state.sides, otherIndex, oldSettings), sides: cleanSet(this.state.sides, otherIndex, oldSettings),
}); });
} };
private onPreprocessorChange = async ( private onPreprocessorChange = async (
preprocessorState: PreprocessorState, preprocessorState: PreprocessorState,
@@ -483,6 +483,29 @@ export default class Compress extends Component<Props, State> {
})); }));
}; };
private onCopyCliClick = async (index: 0 | 1) => {
try {
const cliInvocation = generateCliInvocation(
this.state.sides[index].latestSettings.encoderState!,
this.state.sides[index].latestSettings.processorState,
);
await navigator.clipboard.writeText(cliInvocation);
const result = await this.props.showSnack(
'CLI command copied to clipboard',
{
timeout: 8000,
actions: ['usage', 'dismiss'],
},
);
if (result === 'usage') {
open('https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli');
}
} catch (e) {
this.props.showSnack(e);
}
};
/** /**
* Debounce the heavy lifting of updateImage. * Debounce the heavy lifting of updateImage.
* Otherwise, the thrashing causes jank, and sometimes crashes iOS Safari. * Otherwise, the thrashing causes jank, and sometimes crashes iOS Safari.
@@ -860,6 +883,8 @@ export default class Compress extends Component<Props, State> {
onEncoderTypeChange={this.onEncoderTypeChange} onEncoderTypeChange={this.onEncoderTypeChange}
onEncoderOptionsChange={this.onEncoderOptionsChange} onEncoderOptionsChange={this.onEncoderOptionsChange}
onProcessorOptionsChange={this.onProcessorOptionsChange} onProcessorOptionsChange={this.onProcessorOptionsChange}
onCopyCliClick={this.onCopyCliClick}
onCopyToOtherSideClick={this.onCopyToOtherClick}
/> />
)); ));

View File

@@ -124,3 +124,14 @@ export const DownloadIcon = () => (
<path d="M6.6 2.7h-4v13.2h2.7A2.7 2.7 0 018 18.6a2.7 2.7 0 002.6 2.6h2.7a2.7 2.7 0 002.6-2.6 2.7 2.7 0 012.7-2.7h2.6V2.7h-4a1.3 1.3 0 110-2.7h4A2.7 2.7 0 0124 2.7v18.5a2.7 2.7 0 01-2.7 2.7H2.7A2.7 2.7 0 010 21.2V2.7A2.7 2.7 0 012.7 0h4a1.3 1.3 0 010 2.7zm4 7.4V1.3a1.3 1.3 0 112.7 0v8.8L15 8.4a1.3 1.3 0 011.9 1.8l-4 4a1.3 1.3 0 01-1.9 0l-4-4A1.3 1.3 0 019 8.4z" /> <path d="M6.6 2.7h-4v13.2h2.7A2.7 2.7 0 018 18.6a2.7 2.7 0 002.6 2.6h2.7a2.7 2.7 0 002.6-2.6 2.7 2.7 0 012.7-2.7h2.6V2.7h-4a1.3 1.3 0 110-2.7h4A2.7 2.7 0 0124 2.7v18.5a2.7 2.7 0 01-2.7 2.7H2.7A2.7 2.7 0 010 21.2V2.7A2.7 2.7 0 012.7 0h4a1.3 1.3 0 010 2.7zm4 7.4V1.3a1.3 1.3 0 112.7 0v8.8L15 8.4a1.3 1.3 0 011.9 1.8l-4 4a1.3 1.3 0 01-1.9 0l-4-4A1.3 1.3 0 019 8.4z" />
</svg> </svg>
); );
export const CLIIcon = () => (
<svg viewBox="0 0 81.3 68.8">
<path
fill="none"
stroke-miterlimit="15.6"
stroke-width="6.3"
d="M3.1 3.1h75v62.5h-75zm18.8 43.8l12.5-12.5-12.5-12.5m18.7 25h18.8"
/>
</svg>
);

View File

@@ -0,0 +1,51 @@
/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EncoderState, ProcessorState } from '../feature-meta';
// Maps our encoder.type values to CLI parameter names
const typeMap = new Map<string, string>([
['avif', '--avif'],
['jxl', '--jxl'],
['mozJPEG', '--mozjpeg'],
['oxiPNG', '--oxipng'],
['webP', '--webp'],
['wp2', '--wp2'],
]);
// Same as JSON.stringify, but with single quotes around the entire value
// so that shells dont do weird stuff.
function cliJson<T>(v: T): string {
return "'" + JSON.stringify(v) + "'";
}
export function generateCliInvocation(
encoder: EncoderState,
processor: ProcessorState,
): string {
if (!typeMap.has(encoder.type)) {
throw Error(`Encoder ${encoder.type} is unsupported in the CLI`);
}
return [
'npx',
'@squoosh/cli',
...(processor.resize.enabled
? ['--resize', cliJson(processor.resize)]
: []),
...(processor.quantize.enabled
? ['--quant', cliJson(processor.quantize)]
: []),
typeMap.get(encoder.type)!,
cliJson(encoder.options),
].join(' ');
}

137
src/copy/sw-bridge.894ac.js Normal file
View File

@@ -0,0 +1,137 @@
(self.webpackJsonp = self.webpackJsonp || []).push([
[0],
{
54: function (e, t, n) {
'use strict';
n.d(t, 'a', function () {
return o;
}),
n.d(t, 'b', function () {
return s;
});
class r {
constructor(e = 'keyval-store', t = 'keyval') {
(this.storeName = t),
(this._dbp = new Promise((n, r) => {
const a = indexedDB.open(e, 1);
(a.onerror = () => r(a.error)),
(a.onsuccess = () => n(a.result)),
(a.onupgradeneeded = () => {
a.result.createObjectStore(t);
});
}));
}
_withIDBStore(e, t) {
return this._dbp.then(
(n) =>
new Promise((r, a) => {
const i = n.transaction(this.storeName, e);
(i.oncomplete = () => r()),
(i.onabort = i.onerror = () => a(i.error)),
t(i.objectStore(this.storeName));
}),
);
}
}
let a;
function i() {
return a || (a = new r()), a;
}
function o(e, t = i()) {
let n;
return t
._withIDBStore('readonly', (t) => {
n = t.get(e);
})
.then(() => n.result);
}
function s(e, t, n = i()) {
return n._withIDBStore('readwrite', (n) => {
n.put(t, e);
});
}
},
56: function (e, t, n) {
'use strict';
n.r(t),
function (e) {
n.d(t, 'getSharedImage', function () {
return i;
}),
n.d(t, 'offliner', function () {
return o;
}),
n.d(t, 'mainAppLoaded', function () {
return s;
});
var r = n(54);
async function a(e) {
if (e.waiting) return;
const t = await (async function (e) {
return e.installing
? e.installing
: new Promise((t) => {
e.addEventListener('updatefound', () => t(e.installing), {
once: !0,
});
});
})(e);
return new Promise((e) => {
t.addEventListener('statechange', () => {
'installed' === t.state && e();
});
});
}
function i() {
return new Promise((e) => {
const t = (n) => {
'load-image' === n.data.action &&
(e(n.data.file),
navigator.serviceWorker.removeEventListener('message', t));
};
navigator.serviceWorker.addEventListener('message', t),
navigator.serviceWorker.controller.postMessage('share-ready');
});
}
async function o(t) {
if ('boolean' == typeof PRERENDER) return;
navigator.serviceWorker.register(e);
const n = !!navigator.serviceWorker.controller;
if (
(navigator.serviceWorker.addEventListener(
'controllerchange',
async () => {
n
? location.reload()
: t('Ready to work offline', { timeout: 5e3 });
},
),
!n)
)
return;
const r = await navigator.serviceWorker.getRegistration();
r &&
(await a(r),
'reload' ===
(await t('Update available', {
actions: ['reload', 'dismiss'],
})) &&
(async function () {
const e = await navigator.serviceWorker.getRegistration();
e && e.waiting && e.waiting.postMessage('skip-waiting');
})());
}
async function s() {
if (await Object(r.a)('user-interacted')) return;
Object(r.b)('user-interacted', !0);
const e = await (async function () {
const e = await navigator.serviceWorker.getRegistration();
return e ? e.active || e.waiting || e.installing : null;
})();
e && e.postMessage('cache-all');
}
}.call(this, n.p + 'serviceworker.js');
},
},
]);
//# sourceMappingURL=sw-bridge.894ac.js.map

4
src/copy/sw.js Normal file
View File

@@ -0,0 +1,4 @@
// I accidentally shipped with the wrong service worker name.
// This picks up users that still might be using that version.
// We'll be able to delete this file eventually.
skipWaiting();

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -351,6 +351,12 @@ export default class Intro extends Component<Props, State> {
> >
Privacy Privacy
</a> </a>
<a
class={style.footerLink}
href="https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli"
>
Squoosh CLI
</a>
<a <a
class={style.footerLinkWithLogo} class={style.footerLinkWithLogo}
href="https://github.com/GoogleChromeLabs/squoosh" href="https://github.com/GoogleChromeLabs/squoosh"

View File

@@ -133,11 +133,17 @@
.footer-items { .footer-items {
display: grid; display: grid;
justify-content: end;
grid-auto-columns: max-content; grid-auto-rows: max-content;
grid-auto-flow: column; justify-items: center;
align-items: center; gap: 2rem;
gap: 4rem;
@media (min-width: 480px) {
justify-content: end;
grid-auto-flow: column;
align-items: center;
gap: 4rem;
}
} }
.footer-link { .footer-link {

View File

@@ -28,7 +28,7 @@ const branchOriginTrialIds = new Map([
], ],
[ [
'live', 'live',
'AgoKiDqjr0GVPtrwV/vuVlrrSvbDa5Yb99s+q66ly816DrrAQ8Cdas33NgDtmhxM4BtDP9PEdyuxHPyTQHD5ZAcAAABUeyJvcmlnaW4iOiJodHRwczovL3NxdW9vc2guYXBwOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseVNpbWQiLCJleHBpcnkiOjE2MDg2NzI5OTR9', 'Ak9YMaDZyWUUZFbVJng8FM2LWWNeBcWaHTtHzzaTAq044kMlQH5/hsMb/90Ii2I7m/lPx8EpgOIUMWkWeoaKfgIAAABUeyJvcmlnaW4iOiJodHRwczovL3NxdW9vc2guYXBwOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseVNpbWQiLCJleHBpcnkiOjE2MTExNTkwNjZ9',
], ],
]); ]);