forked from external-repos/squoosh
Merge upstream
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ node_modules
|
||||
*.css.d.ts
|
||||
build
|
||||
*.o
|
||||
.DS_Store
|
||||
|
||||
# Auto-generated by lib/feature-plugin.js
|
||||
src/features-worker/index.ts
|
||||
|
||||
BIN
cli/.DS_Store
vendored
BIN
cli/.DS_Store
vendored
Binary file not shown.
1
cli/.gitignore
vendored
1
cli/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
build
|
||||
.DS_Store
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
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
2
cli/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@squoosh/cli",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@squoosh/cli",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"description": "A CLI for Squoosh",
|
||||
"public": true,
|
||||
"bin": {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isMainThread } from 'worker_threads';
|
||||
import { cpus } from 'os';
|
||||
import { extname, join, basename } from 'path';
|
||||
import { promises as fsp } from 'fs';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { version } from 'json:../package.json';
|
||||
import ora from 'ora';
|
||||
import kleur from 'kleur';
|
||||
@@ -182,7 +183,32 @@ function progressTracker(results) {
|
||||
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) {
|
||||
files = await checkInputFilesValid(files);
|
||||
|
||||
const parallelism = cpus().length;
|
||||
|
||||
const results = new Map();
|
||||
|
||||
12
cli/tmp.txt
12
cli/tmp.txt
File diff suppressed because one or more lines are too long
@@ -112,7 +112,9 @@ export default async function ({ watch }) {
|
||||
plugins: [
|
||||
{ resolveFileUrl, resolveImportMeta },
|
||||
OMT({ loader: await omtLoaderPromise }),
|
||||
serviceWorkerPlugin({ output: 'static/sw.js' }),
|
||||
serviceWorkerPlugin({
|
||||
output: 'static/serviceworker.js',
|
||||
}),
|
||||
...commonPlugins(),
|
||||
commonjs(),
|
||||
resolve(),
|
||||
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
encoderMap,
|
||||
} from '../../feature-meta';
|
||||
import Expander from './Expander';
|
||||
import Checkbox from './Checkbox';
|
||||
import Toggle from './Toggle';
|
||||
import Select from './Select';
|
||||
import { Options as QuantOptionsComponent } from 'features/processors/quantize/client';
|
||||
import { Options as ResizeOptionsComponent } from 'features/processors/resize/client';
|
||||
import { CLIIcon, SwapIcon } from 'client/lazy-app/icons';
|
||||
|
||||
interface Props {
|
||||
index: 0 | 1;
|
||||
@@ -28,6 +28,8 @@ interface Props {
|
||||
onEncoderTypeChange(index: 0 | 1, newType: OutputType): void;
|
||||
onEncoderOptionsChange(index: 0 | 1, newOptions: EncoderOptions): void;
|
||||
onProcessorOptionsChange(index: 0 | 1, newOptions: ProcessorState): void;
|
||||
onCopyToOtherSideClick(index: 0 | 1): void;
|
||||
onCopyCliClick(index: 0 | 1): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -106,6 +108,14 @@ export default class Options extends Component<Props, State> {
|
||||
this.props.onEncoderOptionsChange(this.props.index, newOptions);
|
||||
};
|
||||
|
||||
private onCopyCliClick = () => {
|
||||
this.props.onCopyCliClick(this.props.index);
|
||||
};
|
||||
|
||||
private onCopyToOtherSideClick = () => {
|
||||
this.props.onCopyToOtherSideClick(this.props.index);
|
||||
};
|
||||
|
||||
render(
|
||||
{ source, encoderState, processorState }: Props,
|
||||
{ supportedEncoderMap }: State,
|
||||
@@ -125,7 +135,25 @@ export default class Options extends Component<Props, State> {
|
||||
<Expander>
|
||||
{!encoderState ? null : (
|
||||
<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}>
|
||||
Resize
|
||||
<Toggle
|
||||
|
||||
@@ -81,3 +81,37 @@
|
||||
box-sizing: border-box;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ export default class Output extends Component<Props, State> {
|
||||
</div>
|
||||
|
||||
<div class={style.controls} hidden={hidden}>
|
||||
<div class={style.zoomControls}>
|
||||
<div class={style.buttonGroup}>
|
||||
<button class={style.button} onClick={this.zoomOut}>
|
||||
<RemoveIcon />
|
||||
</button>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-controls {
|
||||
.button-group {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
|
||||
@@ -411,7 +411,7 @@ export default class Compress extends Component<Props, State> {
|
||||
this.queueUpdateImage();
|
||||
}
|
||||
|
||||
private async onCopyToOtherClick(index: 0 | 1) {
|
||||
private onCopyToOtherClick = async (index: 0 | 1) => {
|
||||
const otherIndex = index ? 0 : 1;
|
||||
const oldSettings = this.state.sides[otherIndex];
|
||||
const newSettings = { ...this.state.sides[index] };
|
||||
@@ -436,7 +436,7 @@ export default class Compress extends Component<Props, State> {
|
||||
this.setState({
|
||||
sides: cleanSet(this.state.sides, otherIndex, oldSettings),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private onPreprocessorChange = async (
|
||||
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.
|
||||
* 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}
|
||||
onEncoderOptionsChange={this.onEncoderOptionsChange}
|
||||
onProcessorOptionsChange={this.onProcessorOptionsChange}
|
||||
onCopyCliClick={this.onCopyCliClick}
|
||||
onCopyToOtherSideClick={this.onCopyToOtherClick}
|
||||
/>
|
||||
));
|
||||
|
||||
|
||||
@@ -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" />
|
||||
</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>
|
||||
);
|
||||
|
||||
51
src/client/lazy-app/util/cli.ts
Normal file
51
src/client/lazy-app/util/cli.ts
Normal 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 don’t 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
137
src/copy/sw-bridge.894ac.js
Normal 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
4
src/copy/sw.js
Normal 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 |
@@ -351,6 +351,12 @@ export default class Intro extends Component<Props, State> {
|
||||
>
|
||||
Privacy
|
||||
</a>
|
||||
<a
|
||||
class={style.footerLink}
|
||||
href="https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli"
|
||||
>
|
||||
Squoosh CLI
|
||||
</a>
|
||||
<a
|
||||
class={style.footerLinkWithLogo}
|
||||
href="https://github.com/GoogleChromeLabs/squoosh"
|
||||
|
||||
@@ -133,11 +133,17 @@
|
||||
|
||||
.footer-items {
|
||||
display: grid;
|
||||
|
||||
grid-auto-rows: max-content;
|
||||
justify-items: center;
|
||||
gap: 2rem;
|
||||
|
||||
@media (min-width: 480px) {
|
||||
justify-content: end;
|
||||
grid-auto-columns: max-content;
|
||||
grid-auto-flow: column;
|
||||
align-items: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
|
||||
@@ -28,7 +28,7 @@ const branchOriginTrialIds = new Map([
|
||||
],
|
||||
[
|
||||
'live',
|
||||
'AgoKiDqjr0GVPtrwV/vuVlrrSvbDa5Yb99s+q66ly816DrrAQ8Cdas33NgDtmhxM4BtDP9PEdyuxHPyTQHD5ZAcAAABUeyJvcmlnaW4iOiJodHRwczovL3NxdW9vc2guYXBwOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseVNpbWQiLCJleHBpcnkiOjE2MDg2NzI5OTR9',
|
||||
'Ak9YMaDZyWUUZFbVJng8FM2LWWNeBcWaHTtHzzaTAq044kMlQH5/hsMb/90Ii2I7m/lPx8EpgOIUMWkWeoaKfgIAAABUeyJvcmlnaW4iOiJodHRwczovL3NxdW9vc2guYXBwOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseVNpbWQiLCJleHBpcnkiOjE2MTExNTkwNjZ9',
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user