@arraypress/extension-kit 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/build/index.d.ts +1 -0
- package/src/build/index.js +1 -0
- package/src/build/promo-tile.d.ts +75 -0
- package/src/build/promo-tile.js +87 -0
package/README.md
CHANGED
|
@@ -713,6 +713,7 @@ A list with an Add button needs every key but `optionsRemoveLabel`; one with an
|
|
|
713
713
|
| `testBuildDir(root, name)` | `.test-builds/<name>`, or under `$EXTENSION_TEST_BUILDS` (to test another build, or run two side by side). |
|
|
714
714
|
| `storeFrame` | Store screenshots: `canvas`, `windowChrome` (and `windowChromeSvg`, from `frame/window.js`), `geometry`, `letterIcon`, `cursor`, `THEMES`, `W`×`H`. |
|
|
715
715
|
| `storeShots({browser, dir, toolbarIcon})`, `storeShotHtml(shot)`, `contextMenuHtml(menu)`, `pngUrl(png)`, `PANEL_W`, `PANEL_HEADER_H` | The family's store screenshots in one style: a caption in sentence case over Chrome's light window with the extension pinned in the toolbar, real captures (taken at 2×) inside; what headless Chrome can't capture composed around the real extension UI: the popup page hung from the pressed toolbar button, the side panel page beside the page under a header with the extension's name, the right-click menu drawn as macOS draws Chrome's (the extension's real titles). `shoot(file, shot)` writes a 1280×800 PNG. |
|
|
716
|
+
| `storePromoTile({browser, extension, file, line, glow?, name?, icon?})`, `promoTileHtml(tile)`, `PROMO_W`, `PROMO_H` | The store's small promo tile (440×280) in the family's one style: the extension's `icons/icon.svg` with a soft glow (its colour `glow`, default Chrome's blue), its English `extShortName` and one short `line`, on the store screenshots' light background. Writes the PNG and returns its path. |
|
|
716
717
|
| `placeholderWarning`, `repositoryReminder`, `printReleaseChecks` | See About links. |
|
|
717
718
|
| `findDisallowedClones({runJscpd})` | Run jscpd and return clones `.jscpd-allowlist.json` doesn't cover (the `extension-kit duplication` command). |
|
|
718
719
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arraypress/extension-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Shared pieces for Chrome extensions: Chrome's own page styles and components, a Chrome Sync settings store, the light/dark toolbar glyph, Chrome's on-device model, content-script helpers, About links, and build and test helpers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/build/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { clonesFromReport, disallowedClones, findDisallowedClones } from './duplication.js';
|
|
8
8
|
export { placeholderWarning, printReleaseChecks, repositoryReminder } from './release-checks.js';
|
|
9
|
+
export { PROMO_H, PROMO_W, promoTileHtml, storePromoTile } from './promo-tile.js';
|
|
9
10
|
export * as storeFrame from './store-frame.js';
|
|
10
11
|
export { PANEL_HEADER_H, PANEL_W, contextMenuHtml, pngUrl, storeShotHtml, storeShots } from './store-shots.js';
|
|
11
12
|
export { HOOK_FILE, PANEL_OPENER, TEST_BUILDS_ENV, buildTestCopy, testBuildDir } from './test-build.js';
|
package/src/build/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
export { clonesFromReport, disallowedClones, findDisallowedClones } from './duplication.js';
|
|
9
9
|
export { placeholderWarning, printReleaseChecks, repositoryReminder } from './release-checks.js';
|
|
10
|
+
export { PROMO_H, PROMO_W, promoTileHtml, storePromoTile } from './promo-tile.js';
|
|
10
11
|
export * as storeFrame from './store-frame.js';
|
|
11
12
|
export { PANEL_HEADER_H, PANEL_W, contextMenuHtml, pngUrl, storeShotHtml, storeShots } from './store-shots.js';
|
|
12
13
|
export { HOOK_FILE, PANEL_OPENER, TEST_BUILDS_ENV, buildTestCopy, testBuildDir } from './test-build.js';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Chrome Web Store's small promo tile (440×280) for the whole family, in one style: the
|
|
3
|
+
* extension's icon (drawn from its SVG, so it's sharp at any size) with a soft glow, its name
|
|
4
|
+
* and one short line, on the store screenshots' light background.
|
|
5
|
+
*
|
|
6
|
+
* {@link promoTileHtml} lays it out (pure); {@link storePromoTile} reads the icon and name from
|
|
7
|
+
* the extension folder and writes the PNG.
|
|
8
|
+
*
|
|
9
|
+
* @module build/promo-tile
|
|
10
|
+
*/
|
|
11
|
+
/** The promo tile's width. */
|
|
12
|
+
export declare const PROMO_W = 440;
|
|
13
|
+
/** The promo tile's height. */
|
|
14
|
+
export declare const PROMO_H = 280;
|
|
15
|
+
export type PromoTile = {
|
|
16
|
+
/**
|
|
17
|
+
* The icon: SVG markup, or an image's URL (a data: URL, say).
|
|
18
|
+
*/
|
|
19
|
+
icon: string;
|
|
20
|
+
/**
|
|
21
|
+
* The extension's name (`extShortName`).
|
|
22
|
+
*/
|
|
23
|
+
name: string;
|
|
24
|
+
/**
|
|
25
|
+
* One short line under it, sentence case (“Prices in your currency”).
|
|
26
|
+
*/
|
|
27
|
+
line: string;
|
|
28
|
+
/**
|
|
29
|
+
* The colour of the glow under the icon, any CSS colour: the icon's own (default Chrome's blue).
|
|
30
|
+
*/
|
|
31
|
+
glow?: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* One promo tile.
|
|
35
|
+
*
|
|
36
|
+
* @typedef {object} PromoTile
|
|
37
|
+
* @property {string} icon The icon: SVG markup, or an image's URL (a data: URL, say).
|
|
38
|
+
* @property {string} name The extension's name (`extShortName`).
|
|
39
|
+
* @property {string} line One short line under it, sentence case (“Prices in your currency”).
|
|
40
|
+
* @property {string} [glow] The colour of the glow under the icon, any CSS colour: the icon's own (default Chrome's blue).
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* The tile's page: the icon, the name and the line, centred, at 440×280.
|
|
44
|
+
*
|
|
45
|
+
* @param {PromoTile} tile - What it shows.
|
|
46
|
+
* @returns {string} A whole HTML document.
|
|
47
|
+
* @example
|
|
48
|
+
* promoTileHtml({ icon: svg, name: 'Better Prices', line: 'Prices in your currency', glow: '#146c2e' });
|
|
49
|
+
*/
|
|
50
|
+
export declare function promoTileHtml({ icon, name, line, glow }: PromoTile): string;
|
|
51
|
+
/**
|
|
52
|
+
* Write an extension's promo tile: its `icons/icon.svg` and English `extShortName` (unless
|
|
53
|
+
* `icon` or `name` is given), drawn by {@link promoTileHtml} in a page of the given browser.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} options - What and where.
|
|
56
|
+
* @param {import('playwright').Browser | import('playwright').BrowserContext} options.browser - A browser (or a context) to draw in.
|
|
57
|
+
* @param {string} options.extension - The extension's folder (`extension`, or `src` where a build bundles it).
|
|
58
|
+
* @param {string} options.file - The PNG to write (its folder is made if missing), e.g. `store/promo-440x280.png`.
|
|
59
|
+
* @param {string} options.line - The line under the name.
|
|
60
|
+
* @param {string} [options.glow] - The glow's colour (see {@link PromoTile}).
|
|
61
|
+
* @param {string} [options.name] - The name, if not the extension's `extShortName`.
|
|
62
|
+
* @param {string} [options.icon] - The icon, if not the extension's `icons/icon.svg`.
|
|
63
|
+
* @returns {Promise<string>} The file written.
|
|
64
|
+
* @example
|
|
65
|
+
* await storePromoTile({ browser, extension: 'extension', file: 'store/promo-440x280.png', line: 'Prices in your currency' });
|
|
66
|
+
*/
|
|
67
|
+
export declare function storePromoTile({ browser, extension, file, line, glow, name, icon }: {
|
|
68
|
+
browser: import('playwright').Browser | import('playwright').BrowserContext;
|
|
69
|
+
extension: string;
|
|
70
|
+
file: string;
|
|
71
|
+
line: string;
|
|
72
|
+
glow?: string;
|
|
73
|
+
name?: string;
|
|
74
|
+
icon?: string;
|
|
75
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Chrome Web Store's small promo tile (440×280) for the whole family, in one style: the
|
|
3
|
+
* extension's icon (drawn from its SVG, so it's sharp at any size) with a soft glow, its name
|
|
4
|
+
* and one short line, on the store screenshots' light background.
|
|
5
|
+
*
|
|
6
|
+
* {@link promoTileHtml} lays it out (pure); {@link storePromoTile} reads the icon and name from
|
|
7
|
+
* the extension folder and writes the PNG.
|
|
8
|
+
*
|
|
9
|
+
* @module build/promo-tile
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { esc } from '../frame/window.js';
|
|
15
|
+
import { THEMES } from './store-frame.js';
|
|
16
|
+
|
|
17
|
+
/** The promo tile's width. */
|
|
18
|
+
export const PROMO_W = 440;
|
|
19
|
+
/** The promo tile's height. */
|
|
20
|
+
export const PROMO_H = 280;
|
|
21
|
+
|
|
22
|
+
/** The glow under the icon when none is given: Chrome's blue. */
|
|
23
|
+
const GLOW = '#0b57d0';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* One promo tile.
|
|
27
|
+
*
|
|
28
|
+
* @typedef {object} PromoTile
|
|
29
|
+
* @property {string} icon The icon: SVG markup, or an image's URL (a data: URL, say).
|
|
30
|
+
* @property {string} name The extension's name (`extShortName`).
|
|
31
|
+
* @property {string} line One short line under it, sentence case (“Prices in your currency”).
|
|
32
|
+
* @property {string} [glow] The colour of the glow under the icon, any CSS colour: the icon's own (default Chrome's blue).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The tile's page: the icon, the name and the line, centred, at 440×280.
|
|
37
|
+
*
|
|
38
|
+
* @param {PromoTile} tile - What it shows.
|
|
39
|
+
* @returns {string} A whole HTML document.
|
|
40
|
+
* @example
|
|
41
|
+
* promoTileHtml({ icon: svg, name: 'Better Prices', line: 'Prices in your currency', glow: '#146c2e' });
|
|
42
|
+
*/
|
|
43
|
+
export function promoTileHtml({ icon, name, line, glow = GLOW }) {
|
|
44
|
+
const art = icon.trimStart().startsWith('<') ? icon : `<img src="${icon}" alt="">`;
|
|
45
|
+
return `<!doctype html><html><head><meta charset="utf-8"><style>
|
|
46
|
+
html, body { margin: 0; }
|
|
47
|
+
body { width: ${PROMO_W}px; height: ${PROMO_H}px; overflow: hidden; display: grid; place-items: center; background: linear-gradient(160deg, ${THEMES.light.bg}, #dde3ea); -webkit-font-smoothing: antialiased; font-family: system-ui; }
|
|
48
|
+
.wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; }
|
|
49
|
+
.icon { width: 112px; height: 112px; filter: drop-shadow(0 6px 14px color-mix(in srgb, ${glow} 28%, transparent)); }
|
|
50
|
+
.icon svg, .icon img { display: block; width: 100%; height: 100%; }
|
|
51
|
+
h1 { margin: 0; font: 600 30px/1.1 system-ui; color: #1f1f1f; letter-spacing: -.3px; }
|
|
52
|
+
p { margin: 0; font: 400 17px system-ui; color: #444746; }
|
|
53
|
+
</style></head><body><div class="wrap"><div class="icon">${art}</div><h1>${esc(name)}</h1><p>${esc(line)}</p></div></body></html>`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Write an extension's promo tile: its `icons/icon.svg` and English `extShortName` (unless
|
|
58
|
+
* `icon` or `name` is given), drawn by {@link promoTileHtml} in a page of the given browser.
|
|
59
|
+
*
|
|
60
|
+
* @param {object} options - What and where.
|
|
61
|
+
* @param {import('playwright').Browser | import('playwright').BrowserContext} options.browser - A browser (or a context) to draw in.
|
|
62
|
+
* @param {string} options.extension - The extension's folder (`extension`, or `src` where a build bundles it).
|
|
63
|
+
* @param {string} options.file - The PNG to write (its folder is made if missing), e.g. `store/promo-440x280.png`.
|
|
64
|
+
* @param {string} options.line - The line under the name.
|
|
65
|
+
* @param {string} [options.glow] - The glow's colour (see {@link PromoTile}).
|
|
66
|
+
* @param {string} [options.name] - The name, if not the extension's `extShortName`.
|
|
67
|
+
* @param {string} [options.icon] - The icon, if not the extension's `icons/icon.svg`.
|
|
68
|
+
* @returns {Promise<string>} The file written.
|
|
69
|
+
* @example
|
|
70
|
+
* await storePromoTile({ browser, extension: 'extension', file: 'store/promo-440x280.png', line: 'Prices in your currency' });
|
|
71
|
+
*/
|
|
72
|
+
export async function storePromoTile({ browser, extension, file, line, glow, name, icon }) {
|
|
73
|
+
const svg = icon ?? await readFile(path.join(extension, 'icons', 'icon.svg'), 'utf8');
|
|
74
|
+
const title = name ?? JSON.parse(await readFile(path.join(extension, '_locales', 'en', 'messages.json'), 'utf8')).extShortName.message;
|
|
75
|
+
const context = 'newContext' in browser ? await browser.newContext({ viewport: { width: PROMO_W, height: PROMO_H }, deviceScaleFactor: 1 }) : null;
|
|
76
|
+
const page = context ? await context.newPage() : await /** @type {import('playwright').BrowserContext} */ (browser).newPage();
|
|
77
|
+
try {
|
|
78
|
+
await page.setViewportSize({ width: PROMO_W, height: PROMO_H });
|
|
79
|
+
await page.setContent(promoTileHtml({ icon: svg, name: String(title), line, glow }), { waitUntil: 'load' });
|
|
80
|
+
await page.evaluate(() => Promise.all([...document.images].map((img) => img.decode().catch(() => undefined))));
|
|
81
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
82
|
+
await writeFile(file, await page.screenshot({ type: 'png', scale: 'css', clip: { x: 0, y: 0, width: PROMO_W, height: PROMO_H } }));
|
|
83
|
+
return file;
|
|
84
|
+
} finally {
|
|
85
|
+
await (context ?? page).close();
|
|
86
|
+
}
|
|
87
|
+
}
|