mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-12 16:57:26 +00:00
Compare commits
1 Commits
dependabot
...
no-inline-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94157a3ef3 |
@@ -1,7 +1,3 @@
|
|||||||
# Project no longer maintained
|
|
||||||
|
|
||||||
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
|
|
||||||
|
|
||||||
# Squoosh CLI
|
# Squoosh CLI
|
||||||
|
|
||||||
Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
||||||
|
|||||||
4
cli/package-lock.json
generated
4
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/cli",
|
"name": "@squoosh/cli",
|
||||||
"version": "0.7.3",
|
"version": "0.7.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@squoosh/cli",
|
"name": "@squoosh/cli",
|
||||||
"version": "0.7.3",
|
"version": "0.7.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@squoosh/lib": "^0.4.0",
|
"@squoosh/lib": "^0.4.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/cli",
|
"name": "@squoosh/cli",
|
||||||
"version": "0.7.3",
|
"version": "0.7.2",
|
||||||
"description": "A CLI for Squoosh",
|
"description": "A CLI for Squoosh",
|
||||||
"public": true,
|
"public": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -11,10 +11,23 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
import { promises as fsp, readFileSync } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { posix } from 'path';
|
import { posix } from 'path';
|
||||||
import glob from 'glob';
|
import glob from 'glob';
|
||||||
|
import postcss from 'postcss';
|
||||||
|
import postCSSNested from 'postcss-nested';
|
||||||
|
import postCSSUrl from 'postcss-url';
|
||||||
|
import postCSSModules from 'postcss-modules';
|
||||||
|
import postCSSSimpleVars from 'postcss-simple-vars';
|
||||||
|
import cssNano from 'cssnano';
|
||||||
|
import {
|
||||||
|
parse as parsePath,
|
||||||
|
resolve as resolvePath,
|
||||||
|
dirname,
|
||||||
|
normalize as nomalizePath,
|
||||||
|
sep as pathSep,
|
||||||
|
} from 'path';
|
||||||
|
|
||||||
const globP = promisify(glob);
|
const globP = promisify(glob);
|
||||||
|
|
||||||
@@ -30,26 +43,72 @@ export default function initialCssPlugin() {
|
|||||||
async load(id) {
|
async load(id) {
|
||||||
if (id !== initialCssModule) return;
|
if (id !== initialCssModule) return;
|
||||||
|
|
||||||
const matches = await globP('shared/prerendered-app/**/*.css', {
|
const matches = (
|
||||||
nodir: true,
|
await globP('shared/prerendered-app/**/*.css', {
|
||||||
cwd: path.join(process.cwd(), 'src'),
|
nodir: true,
|
||||||
});
|
cwd: path.join(process.cwd(), 'src'),
|
||||||
|
absolute: true,
|
||||||
|
})
|
||||||
|
).map((cssPath) =>
|
||||||
|
// glob() returns windows paths with a forward slash. Normalise it:
|
||||||
|
path.normalize(cssPath),
|
||||||
|
);
|
||||||
|
|
||||||
// Sort the matches so the parentmost items appear first.
|
// Sort the matches so the parentmost items appear first.
|
||||||
// This is a bit of a hack, but it means the util stuff appears in the cascade first.
|
// This is a bit of a hack, but it means the util stuff appears in the cascade first.
|
||||||
const sortedMatches = matches
|
const sortedMatches = matches
|
||||||
.map((match) => path.normalize(match).split(path.sep))
|
.map((match) => path.normalize(match).split(path.sep))
|
||||||
.sort((a, b) => a.length - b.length)
|
.sort((a, b) => a.length - b.length)
|
||||||
.map((match) => posix.join(...match));
|
.map((match) => '/' + posix.join(...match));
|
||||||
|
|
||||||
const imports = sortedMatches
|
const cssSources = await Promise.all(
|
||||||
.map((id, i) => `import css${i} from 'css:${id}';\n`)
|
sortedMatches.map(async (path) => {
|
||||||
.join('');
|
this.addWatchFile(path);
|
||||||
|
const file = await fsp.readFile(path);
|
||||||
|
|
||||||
return (
|
const cssResult = await postcss([
|
||||||
imports +
|
postCSSNested,
|
||||||
`export default ${sortedMatches.map((_, i) => `css${i}`).join(' + ')};`
|
postCSSSimpleVars(),
|
||||||
|
postCSSModules({
|
||||||
|
root: '',
|
||||||
|
}),
|
||||||
|
postCSSUrl({
|
||||||
|
url: ({ relativePath, url }) => {
|
||||||
|
if (/^((https?|data):|#)/.test(url)) return url;
|
||||||
|
const parsedPath = parsePath(relativePath);
|
||||||
|
const source = readFileSync(
|
||||||
|
resolvePath(dirname(path), relativePath),
|
||||||
|
);
|
||||||
|
const fileId = this.emitFile({
|
||||||
|
type: 'asset',
|
||||||
|
name: parsedPath.base,
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
const hash = createHash('md5');
|
||||||
|
hash.update(source);
|
||||||
|
const md5 = hash.digest('hex');
|
||||||
|
hashToId.set(md5, fileId);
|
||||||
|
return `/fake/path/to/asset/${md5}/`;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
cssNano,
|
||||||
|
]).process(file, {
|
||||||
|
from: path,
|
||||||
|
});
|
||||||
|
|
||||||
|
return cssResult.css;
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const css = cssSources.join('\n');
|
||||||
|
|
||||||
|
const fileId = this.emitFile({
|
||||||
|
type: 'asset',
|
||||||
|
source: css,
|
||||||
|
name: 'initial.css',
|
||||||
|
});
|
||||||
|
|
||||||
|
return `export default import.meta.ROLLUP_FILE_URL_${fileId};`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
# Project no longer maintained
|
|
||||||
|
|
||||||
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
|
|
||||||
|
|
||||||
# libSquoosh
|
# libSquoosh
|
||||||
|
|
||||||
libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
|
||||||
|
|||||||
16
libsquoosh/package-lock.json
generated
16
libsquoosh/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/lib",
|
"name": "@squoosh/lib",
|
||||||
"version": "0.5.2",
|
"version": "0.4.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@squoosh/lib",
|
"name": "@squoosh/lib",
|
||||||
"version": "0.5.2",
|
"version": "0.4.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wasm-feature-detect": "^1.2.11",
|
"wasm-feature-detect": "^1.2.11",
|
||||||
@@ -1764,9 +1764,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
@@ -3720,9 +3720,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-parse": {
|
"path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"picomatch": {
|
"picomatch": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@squoosh/lib",
|
"name": "@squoosh/lib",
|
||||||
"version": "0.5.2",
|
"version": "0.5.0",
|
||||||
"description": "A Node library for Squoosh",
|
"description": "A Node library for Squoosh",
|
||||||
"public": true,
|
"public": true,
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scrubber {
|
.scrubber {
|
||||||
display: grid;
|
display: flex;
|
||||||
align-content: center;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -55,6 +54,10 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
|
|||||||
padding: 0 calc(var(--thumb-size) * 0.24);
|
padding: 0 calc(var(--thumb-size) * 0.24);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scrubber svg {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.arrow-left {
|
.arrow-left {
|
||||||
fill: var(--pink);
|
fill: var(--pink);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
padding: 9px 66px;
|
padding: 9px 66px;
|
||||||
position: relative;
|
position: relative;
|
||||||
gap: 6px;
|
|
||||||
|
|
||||||
/* Allow clicks to fall through to the pinch zoom area */
|
/* Allow clicks to fall through to the pinch zoom area */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -69,6 +68,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
margin: 0 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button,
|
.button,
|
||||||
@@ -95,7 +95,6 @@
|
|||||||
|
|
||||||
.button {
|
.button {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(50, 50, 50, 0.92);
|
background: rgba(50, 50, 50, 0.92);
|
||||||
@@ -115,7 +114,7 @@
|
|||||||
.last-button {
|
.last-button {
|
||||||
composes: button;
|
composes: button;
|
||||||
border-radius: 0 6px 6px 0;
|
border-radius: 0 6px 6px 0;
|
||||||
border-right-width: 1px;
|
border-left-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom {
|
.zoom {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ async function decodeImage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise fall through and try built-in decoding for a laugh.
|
// Otherwise fall through and try built-in decoding for a laugh.
|
||||||
return await builtinDecode(signal, blob);
|
return await builtinDecode(signal, blob, mimeType);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error && err.name === 'AbortError') throw err;
|
if (err instanceof Error && err.name === 'AbortError') throw err;
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|||||||
@@ -63,17 +63,35 @@ interface DrawableToImageDataOptions {
|
|||||||
sh?: number;
|
sh?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWidth(
|
||||||
|
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
|
||||||
|
): number {
|
||||||
|
if ('displayWidth' in drawable) {
|
||||||
|
return drawable.displayWidth;
|
||||||
|
}
|
||||||
|
return drawable.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeight(
|
||||||
|
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
|
||||||
|
): number {
|
||||||
|
if ('displayHeight' in drawable) {
|
||||||
|
return drawable.displayHeight;
|
||||||
|
}
|
||||||
|
return drawable.height;
|
||||||
|
}
|
||||||
|
|
||||||
export function drawableToImageData(
|
export function drawableToImageData(
|
||||||
drawable: ImageBitmap | HTMLImageElement,
|
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
|
||||||
opts: DrawableToImageDataOptions = {},
|
opts: DrawableToImageDataOptions = {},
|
||||||
): ImageData {
|
): ImageData {
|
||||||
const {
|
const {
|
||||||
width = drawable.width,
|
width = getWidth(drawable),
|
||||||
height = drawable.height,
|
height = getHeight(drawable),
|
||||||
sx = 0,
|
sx = 0,
|
||||||
sy = 0,
|
sy = 0,
|
||||||
sw = drawable.width,
|
sw = getWidth(drawable),
|
||||||
sh = drawable.height,
|
sh = getHeight(drawable),
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
// Make canvas same size as image
|
// Make canvas same size as image
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as WebCodecs from '../util/web-codecs';
|
||||||
import { drawableToImageData } from './canvas';
|
import { drawableToImageData } from './canvas';
|
||||||
|
|
||||||
/** If render engine is Safari */
|
/** If render engine is Safari */
|
||||||
@@ -137,7 +139,15 @@ export async function blobToImg(blob: Blob): Promise<HTMLImageElement> {
|
|||||||
export async function builtinDecode(
|
export async function builtinDecode(
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
blob: Blob,
|
blob: Blob,
|
||||||
|
mimeType: string,
|
||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
|
// If WebCodecs are supported, use that.
|
||||||
|
if (await WebCodecs.isTypeSupported(mimeType)) {
|
||||||
|
assertSignal(signal);
|
||||||
|
try {
|
||||||
|
return await abortable(signal, WebCodecs.decode(blob, mimeType));
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
assertSignal(signal);
|
assertSignal(signal);
|
||||||
|
|
||||||
// Prefer createImageBitmap as it's the off-thread option for Firefox.
|
// Prefer createImageBitmap as it's the off-thread option for Firefox.
|
||||||
|
|||||||
33
src/client/lazy-app/util/web-codecs/index.ts
Normal file
33
src/client/lazy-app/util/web-codecs/index.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { drawableToImageData } from '../canvas';
|
||||||
|
|
||||||
|
const hasImageDecoder = typeof ImageDecoder !== 'undefined';
|
||||||
|
|
||||||
|
export async function isTypeSupported(mimeType: string): Promise<boolean> {
|
||||||
|
if (!hasImageDecoder) return false;
|
||||||
|
// Some old versions of this API threw here.
|
||||||
|
// It only impacted folks with experimental web platform flags enabled in Chrome 90.
|
||||||
|
// The API was updated in Chrome 91.
|
||||||
|
try {
|
||||||
|
return await ImageDecoder.isTypeSupported(mimeType);
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function decode(
|
||||||
|
blob: Blob | File,
|
||||||
|
mimeType: string,
|
||||||
|
): Promise<ImageData> {
|
||||||
|
if (!hasImageDecoder) {
|
||||||
|
throw Error(
|
||||||
|
`This browser does not support ImageDecoder. This function should not have been called.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const decoder = new ImageDecoder({
|
||||||
|
type: mimeType,
|
||||||
|
// Non-obvious way to turn an Blob into a ReadableStream
|
||||||
|
data: new Response(blob).body!,
|
||||||
|
});
|
||||||
|
const { image } = await decoder.decode();
|
||||||
|
return drawableToImageData(image);
|
||||||
|
}
|
||||||
60
src/client/lazy-app/util/web-codecs/missing-types.d.ts
vendored
Normal file
60
src/client/lazy-app/util/web-codecs/missing-types.d.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
interface ImageDecoderInit {
|
||||||
|
type: string;
|
||||||
|
data: BufferSource | ReadableStream;
|
||||||
|
premultiplyAlpha?: PremultiplyAlpha;
|
||||||
|
colorSpaceConversion?: ColorSpaceConversion;
|
||||||
|
desiredWidth?: number;
|
||||||
|
desiredHeight?: number;
|
||||||
|
preferAnimation?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImageDecodeOptions {
|
||||||
|
frameIndex: number;
|
||||||
|
completeFramesOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImageDecodeResult {
|
||||||
|
image: VideoFrame;
|
||||||
|
complete: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I didn’t do all the types because the class is kinda complex.
|
||||||
|
// I focused on what we need.
|
||||||
|
// See https://w3c.github.io/webcodecs/#videoframe
|
||||||
|
declare class VideoFrame {
|
||||||
|
displayWidth: number;
|
||||||
|
displayHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add VideoFrame to canvas’ drawImage()
|
||||||
|
interface CanvasDrawImage {
|
||||||
|
drawImage(
|
||||||
|
image: CanvasImageSource | VideoFrame,
|
||||||
|
dx: number,
|
||||||
|
dy: number,
|
||||||
|
): void;
|
||||||
|
drawImage(
|
||||||
|
image: CanvasImageSource | VideoFrame,
|
||||||
|
dx: number,
|
||||||
|
dy: number,
|
||||||
|
dw: number,
|
||||||
|
dh: number,
|
||||||
|
): void;
|
||||||
|
drawImage(
|
||||||
|
image: CanvasImageSource | VideoFrame,
|
||||||
|
sx: number,
|
||||||
|
sy: number,
|
||||||
|
sw: number,
|
||||||
|
sh: number,
|
||||||
|
dx: number,
|
||||||
|
dy: number,
|
||||||
|
dw: number,
|
||||||
|
dh: number,
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class ImageDecoder {
|
||||||
|
static isTypeSupported(type: string): Promise<boolean>;
|
||||||
|
constructor(desc: ImageDecoderInit);
|
||||||
|
decode(opts?: Partial<ImageDecodeOptions>): Promise<ImageDecodeResult>;
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
@@ -19,39 +19,15 @@ import * as iconLarge from 'img-url:static-build/assets/icon-large.png';
|
|||||||
import * as screenshot1 from 'img-url:static-build/assets/screenshot1.png';
|
import * as screenshot1 from 'img-url:static-build/assets/screenshot1.png';
|
||||||
import * as screenshot2 from 'img-url:static-build/assets/screenshot2.jpg';
|
import * as screenshot2 from 'img-url:static-build/assets/screenshot2.jpg';
|
||||||
import * as screenshot3 from 'img-url:static-build/assets/screenshot3.jpg';
|
import * as screenshot3 from 'img-url:static-build/assets/screenshot3.jpg';
|
||||||
import * as screenshot4 from 'img-url:static-build/assets/screenshot4.png';
|
|
||||||
import * as screenshot5 from 'img-url:static-build/assets/screenshot5.jpg';
|
|
||||||
import * as screenshot6 from 'img-url:static-build/assets/screenshot6.jpg';
|
|
||||||
import dedent from 'dedent';
|
import dedent from 'dedent';
|
||||||
import { lookup as lookupMime } from 'mime-types';
|
import { lookup as lookupMime } from 'mime-types';
|
||||||
|
|
||||||
interface Dimensions {
|
const manifestSize = ({ width, height }: { width: number; height: number }) =>
|
||||||
width: number;
|
`${width}x${height}`;
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const manifestSize = ({ width, height }: Dimensions) => `${width}x${height}`;
|
|
||||||
const formFactor = ({ width, height }: Dimensions) =>
|
|
||||||
width > height ? 'wide' : 'narrow';
|
|
||||||
|
|
||||||
const screenshots = [
|
|
||||||
screenshot1,
|
|
||||||
screenshot2,
|
|
||||||
screenshot3,
|
|
||||||
screenshot4,
|
|
||||||
screenshot5,
|
|
||||||
screenshot6,
|
|
||||||
].map((screenshot) => ({
|
|
||||||
src: screenshot.default,
|
|
||||||
type: lookupMime(screenshot.default),
|
|
||||||
sizes: manifestSize(screenshot),
|
|
||||||
form_factor: formFactor(screenshot),
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface Output {
|
interface Output {
|
||||||
[outputPath: string]: string;
|
[outputPath: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toOutput: Output = {
|
const toOutput: Output = {
|
||||||
'index.html': renderPage(<IndexPage />),
|
'index.html': renderPage(<IndexPage />),
|
||||||
'manifest.json': JSON.stringify({
|
'manifest.json': JSON.stringify({
|
||||||
@@ -79,7 +55,23 @@ const toOutput: Output = {
|
|||||||
'Compress and compare images with different codecs, right in your browser.',
|
'Compress and compare images with different codecs, right in your browser.',
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
categories: ['photo', 'productivity', 'utilities'],
|
categories: ['photo', 'productivity', 'utilities'],
|
||||||
screenshots,
|
screenshots: [
|
||||||
|
{
|
||||||
|
src: screenshot1.default,
|
||||||
|
type: lookupMime(screenshot1.default),
|
||||||
|
sizes: manifestSize(screenshot1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: screenshot2.default,
|
||||||
|
type: lookupMime(screenshot2.default),
|
||||||
|
sizes: manifestSize(screenshot2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: screenshot3.default,
|
||||||
|
type: lookupMime(screenshot3.default),
|
||||||
|
sizes: manifestSize(screenshot3),
|
||||||
|
},
|
||||||
|
],
|
||||||
share_target: {
|
share_target: {
|
||||||
action: '/?utm_medium=PWA&utm_source=share-target&share-target',
|
action: '/?utm_medium=PWA&utm_source=share-target&share-target',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -64,11 +64,7 @@ const Index: FunctionalComponent<Props> = () => (
|
|||||||
<style
|
<style
|
||||||
dangerouslySetInnerHTML={{ __html: escapeStyleScriptContent(baseCss) }}
|
dangerouslySetInnerHTML={{ __html: escapeStyleScriptContent(baseCss) }}
|
||||||
/>
|
/>
|
||||||
<style
|
<link rel="stylesheet" href={initialCss} />
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: escapeStyleScriptContent(initialCss),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|||||||
Reference in New Issue
Block a user