@kernocal/viteshot 0.1.1 → 0.1.2
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/dist/cli.mjs +21 -6
- package/dist/{help-messages-B_-FA6OE.mjs → help-messages-D98KkJBb.mjs} +18 -2
- package/dist/index.d.mts +10 -1
- package/dist/index.mjs +2 -1
- package/dist/{init-BUrgR61N.mjs → init-EKAK797h.mjs} +33 -0
- package/dist/suggest-C2hZd_gx.mjs +105 -0
- package/dist/suggest-D4mx8DKj.mjs +3 -0
- package/package.json +10 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
2
3
|
import { styleText } from "node:util";
|
|
3
4
|
|
|
4
5
|
//#region src/cli.ts
|
|
@@ -11,7 +12,7 @@ function getFlag(name) {
|
|
|
11
12
|
}
|
|
12
13
|
switch (command) {
|
|
13
14
|
case "dev": {
|
|
14
|
-
const { DEV_HELP_MESSAGE } = await import("./help-messages-
|
|
15
|
+
const { DEV_HELP_MESSAGE } = await import("./help-messages-D98KkJBb.mjs");
|
|
15
16
|
if (showHelp) {
|
|
16
17
|
console.log(DEV_HELP_MESSAGE);
|
|
17
18
|
process.exit(0);
|
|
@@ -23,7 +24,7 @@ switch (command) {
|
|
|
23
24
|
break;
|
|
24
25
|
}
|
|
25
26
|
case "export": {
|
|
26
|
-
const { EXPORT_HELP_MESSAGE } = await import("./help-messages-
|
|
27
|
+
const { EXPORT_HELP_MESSAGE } = await import("./help-messages-D98KkJBb.mjs");
|
|
27
28
|
if (showHelp) {
|
|
28
29
|
console.log(EXPORT_HELP_MESSAGE);
|
|
29
30
|
process.exit(0);
|
|
@@ -37,7 +38,7 @@ switch (command) {
|
|
|
37
38
|
process.exit(0);
|
|
38
39
|
}
|
|
39
40
|
case "popup": {
|
|
40
|
-
const { POPUP_HELP_MESSAGE } = await import("./help-messages-
|
|
41
|
+
const { POPUP_HELP_MESSAGE } = await import("./help-messages-D98KkJBb.mjs");
|
|
41
42
|
if (showHelp) {
|
|
42
43
|
console.log(POPUP_HELP_MESSAGE);
|
|
43
44
|
process.exit(0);
|
|
@@ -52,18 +53,32 @@ switch (command) {
|
|
|
52
53
|
});
|
|
53
54
|
process.exit(0);
|
|
54
55
|
}
|
|
56
|
+
case "suggest": {
|
|
57
|
+
const { SUGGEST_HELP_MESSAGE } = await import("./help-messages-D98KkJBb.mjs");
|
|
58
|
+
if (showHelp) {
|
|
59
|
+
console.log(SUGGEST_HELP_MESSAGE);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
const dir = args[0] ?? DEFAULT_DIR;
|
|
63
|
+
const { suggest } = await import("./suggest-D4mx8DKj.mjs");
|
|
64
|
+
await suggest({
|
|
65
|
+
dataSrc: resolve(dir, "data"),
|
|
66
|
+
exportsDir: resolve(dir, "exports")
|
|
67
|
+
});
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
55
70
|
case "init": {
|
|
56
|
-
const { INIT_HELP_MESSAGE } = await import("./help-messages-
|
|
71
|
+
const { INIT_HELP_MESSAGE } = await import("./help-messages-D98KkJBb.mjs");
|
|
57
72
|
if (showHelp) {
|
|
58
73
|
console.log(INIT_HELP_MESSAGE);
|
|
59
74
|
process.exit(0);
|
|
60
75
|
}
|
|
61
|
-
const { init } = await import("./init-
|
|
76
|
+
const { init } = await import("./init-EKAK797h.mjs");
|
|
62
77
|
await init(args[0] ?? DEFAULT_DIR);
|
|
63
78
|
process.exit(0);
|
|
64
79
|
}
|
|
65
80
|
default: {
|
|
66
|
-
const { GENERAL_HELP_MESSAGE } = await import("./help-messages-
|
|
81
|
+
const { GENERAL_HELP_MESSAGE } = await import("./help-messages-D98KkJBb.mjs");
|
|
67
82
|
if (showHelp) {
|
|
68
83
|
console.log(GENERAL_HELP_MESSAGE);
|
|
69
84
|
process.exit(0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { styleText } from "node:util";
|
|
2
2
|
|
|
3
3
|
//#region package.json
|
|
4
|
-
var version = "0.1.
|
|
4
|
+
var version = "0.1.2";
|
|
5
5
|
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/core/help-messages.ts
|
|
@@ -15,6 +15,7 @@ ${styleText("bold", "Commands")}
|
|
|
15
15
|
${styleText(["bold", "magenta"], "export")} ${styleText("dim", "store")} Export screenshots
|
|
16
16
|
${styleText(["bold", "magenta"], "init")} ${styleText("dim", "store")} Initialize viteshot in your project
|
|
17
17
|
${styleText(["bold", "magenta"], "popup")} ${styleText("dim", "--extension-path")} Screenshot a browser extension popup
|
|
18
|
+
${styleText(["bold", "magenta"], "suggest")} ${styleText("dim", "store")} Generate store listing suggestions via AI
|
|
18
19
|
|
|
19
20
|
${styleText("dim", "<command>")} ${styleText(["bold", "blue"], "--help")} Print help text for a command
|
|
20
21
|
`;
|
|
@@ -56,6 +57,21 @@ ${styleText("bold", "Examples:")}
|
|
|
56
57
|
${styleText(["bold", "green"], "viteshot popup")} --extension-path=.output/chrome-mv3
|
|
57
58
|
${styleText(["bold", "green"], "viteshot popup")} --extension-path=.output/chrome-mv3 --output=screenshots
|
|
58
59
|
`;
|
|
60
|
+
const SUGGEST_HELP_MESSAGE = `${styleText("bold", "Usage:")} ${styleText(["bold", "green"], "viteshot suggest")} ${styleText("bold", "[<folder>]")}
|
|
61
|
+
Generate AI suggestions for store listing fields (permission justifications, single purpose, search terms).
|
|
62
|
+
|
|
63
|
+
${styleText("bold", "Args:")}
|
|
64
|
+
|
|
65
|
+
${FOLDER_ARG}
|
|
66
|
+
|
|
67
|
+
${styleText("bold", "Environment:")}
|
|
68
|
+
|
|
69
|
+
${styleText(["bold", "blue"], "GEMINI_API_KEY")} Google AI API key ${styleText("red", "(required)")}
|
|
70
|
+
|
|
71
|
+
${styleText("bold", "Examples:")}
|
|
72
|
+
${styleText(["bold", "green"], "viteshot suggest")}
|
|
73
|
+
${styleText(["bold", "green"], "viteshot suggest")} store
|
|
74
|
+
`;
|
|
59
75
|
const INIT_HELP_MESSAGE = `
|
|
60
76
|
${styleText("bold", "Usage:")} ${styleText(["bold", "green"], "viteshot init")} ${styleText("bold", "[<folder>]")}
|
|
61
77
|
Initialize viteshot in your project.
|
|
@@ -70,4 +86,4 @@ ${styleText("bold", "Examples:")}
|
|
|
70
86
|
`;
|
|
71
87
|
|
|
72
88
|
//#endregion
|
|
73
|
-
export { DEV_HELP_MESSAGE, EXPORT_HELP_MESSAGE, GENERAL_HELP_MESSAGE, INIT_HELP_MESSAGE, POPUP_HELP_MESSAGE };
|
|
89
|
+
export { DEV_HELP_MESSAGE, EXPORT_HELP_MESSAGE, GENERAL_HELP_MESSAGE, INIT_HELP_MESSAGE, POPUP_HELP_MESSAGE, SUGGEST_HELP_MESSAGE };
|
package/dist/index.d.mts
CHANGED
|
@@ -109,4 +109,13 @@ type Screenshot = {
|
|
|
109
109
|
declare function getScreenshots(designsDir: string): Promise<Screenshot[]>;
|
|
110
110
|
declare function logInvalidDesignFiles(designsDir: string): Promise<void>;
|
|
111
111
|
//#endregion
|
|
112
|
-
|
|
112
|
+
//#region src/core/suggest.d.ts
|
|
113
|
+
interface SuggestOptions {
|
|
114
|
+
/** Path to the store data module (resolved by jiti). @default "store/data" */
|
|
115
|
+
dataSrc?: string;
|
|
116
|
+
/** Directory to write suggested-data.json. @default "store/exports" */
|
|
117
|
+
exportsDir?: string;
|
|
118
|
+
}
|
|
119
|
+
declare function suggest(options?: SuggestOptions): Promise<void>;
|
|
120
|
+
//#endregion
|
|
121
|
+
export { CaptureOptions, CaptureResult, CapturedImages, InlineConfig, PopupScreenshotOptions, PuppeteerOptions, ResolvedConfig, Screenshot, SuggestOptions, UserConfig, captureExtensionScreenshots, captureLocale, createServer, defineConfig, exportPopupScreenshot, exportScreenshots, getScreenshots, logInvalidDesignFiles, resolveConfig, suggest };
|
package/dist/index.mjs
CHANGED
|
@@ -3,5 +3,6 @@ import { i as logInvalidDesignFiles, n as captureLocale, r as getScreenshots, t
|
|
|
3
3
|
import { t as createServer } from "./create-server-CpPbF7ms.mjs";
|
|
4
4
|
import { t as exportScreenshots } from "./export-screenshots-B6IGCV4J.mjs";
|
|
5
5
|
import { t as exportPopupScreenshot } from "./export-popup-screenshot-UhxUtKCP.mjs";
|
|
6
|
+
import { t as suggest } from "./suggest-C2hZd_gx.mjs";
|
|
6
7
|
|
|
7
|
-
export { captureExtensionScreenshots, captureLocale, createServer, defineConfig, exportPopupScreenshot, exportScreenshots, getScreenshots, logInvalidDesignFiles, resolveConfig };
|
|
8
|
+
export { captureExtensionScreenshots, captureLocale, createServer, defineConfig, exportPopupScreenshot, exportScreenshots, getScreenshots, logInvalidDesignFiles, resolveConfig, suggest };
|
|
@@ -56,6 +56,39 @@ body {
|
|
|
56
56
|
<p>Screenshot design here</p>
|
|
57
57
|
<p>{{translated}}</p>
|
|
58
58
|
</div>
|
|
59
|
+
`,
|
|
60
|
+
"data.ts": `import type { StoreDetails } from '@kernocal/validate-store'
|
|
61
|
+
|
|
62
|
+
export const storeData = {
|
|
63
|
+
common: {
|
|
64
|
+
privacyPolicyUrl: '',
|
|
65
|
+
screenshots: [{ path: 'exports/screenshot-1.png', width: 1280, height: 800 }],
|
|
66
|
+
smallPromoTile: { path: 'exports/small-promo.png', width: 440, height: 280 },
|
|
67
|
+
largePromoTile: null,
|
|
68
|
+
promoVideoUrl: null,
|
|
69
|
+
websiteUrl: '',
|
|
70
|
+
matureContent: false,
|
|
71
|
+
locales: {
|
|
72
|
+
en: { description: '' },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
chrome: {
|
|
76
|
+
category: 'Tools',
|
|
77
|
+
icon: { path: 'exports/icon-128.png', width: 128, height: 128 },
|
|
78
|
+
officialUrl: null,
|
|
79
|
+
supportUrl: '',
|
|
80
|
+
singlePurpose: '',
|
|
81
|
+
permissionJustifications: {},
|
|
82
|
+
remoteCode: false,
|
|
83
|
+
},
|
|
84
|
+
edge: {
|
|
85
|
+
category: 'Productivity',
|
|
86
|
+
icon: { path: 'exports/icon-300.png', width: 300, height: 300 },
|
|
87
|
+
searchTerms: [],
|
|
88
|
+
supportContact: '',
|
|
89
|
+
collectsPersonalData: false,
|
|
90
|
+
},
|
|
91
|
+
} satisfies StoreDetails
|
|
59
92
|
`
|
|
60
93
|
};
|
|
61
94
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createJiti } from "jiti";
|
|
4
|
+
|
|
5
|
+
//#region src/core/suggest.ts
|
|
6
|
+
async function generatePermissionJustifications(ai, model, permissions, description) {
|
|
7
|
+
const response = await ai.models.generateContent({
|
|
8
|
+
model,
|
|
9
|
+
contents: `You are writing Chrome Web Store permission justifications for a browser extension.
|
|
10
|
+
|
|
11
|
+
Extension description: ${description}
|
|
12
|
+
Permissions requested: ${permissions.join(", ")}
|
|
13
|
+
|
|
14
|
+
For each permission, write a clear, concise justification (1 sentence) explaining
|
|
15
|
+
why the extension needs this permission. Chrome reviewers will read these.
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
- Be specific about what the permission is used for
|
|
19
|
+
- Reference the extension's actual functionality
|
|
20
|
+
- Keep each justification under 200 characters
|
|
21
|
+
- Do not use marketing language
|
|
22
|
+
- Do not start the justification with "to ".
|
|
23
|
+
- Do not start with The x permission or the permission x.
|
|
24
|
+
- Do not end the justification with a period.
|
|
25
|
+
- Do not start the justification with captilisation, first word can be lowercase.
|
|
26
|
+
|
|
27
|
+
Respond as JSON: { "permissionName": "justification text" }`,
|
|
28
|
+
config: {
|
|
29
|
+
responseMimeType: "application/json",
|
|
30
|
+
responseSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: Object.fromEntries(permissions.map((p) => [p, { type: "string" }])),
|
|
33
|
+
required: permissions
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return JSON.parse(response.text ?? "{}");
|
|
38
|
+
}
|
|
39
|
+
async function generateSinglePurpose(ai, model, description) {
|
|
40
|
+
return (await ai.models.generateContent({
|
|
41
|
+
model,
|
|
42
|
+
contents: `You are writing a Chrome Web Store "single purpose" description for a browser extension.
|
|
43
|
+
|
|
44
|
+
Extension description: ${description}
|
|
45
|
+
|
|
46
|
+
Write a concise single-purpose statement (1 sentence) that describes what this extension does.
|
|
47
|
+
Chrome requires this to be a clear, single purpose. Keep it under 132 characters.`
|
|
48
|
+
})).text ?? "";
|
|
49
|
+
}
|
|
50
|
+
async function generateSearchTerms(ai, model, description) {
|
|
51
|
+
const response = await ai.models.generateContent({
|
|
52
|
+
model,
|
|
53
|
+
contents: `Generate search terms for a Microsoft Edge Add-ons listing.
|
|
54
|
+
|
|
55
|
+
Extension description: ${description}
|
|
56
|
+
|
|
57
|
+
Rules:
|
|
58
|
+
- Maximum 7 terms
|
|
59
|
+
- Each term max 30 characters
|
|
60
|
+
- Total max 21 words across all terms
|
|
61
|
+
- Terms should be what users would search to find this extension
|
|
62
|
+
- Include both specific and broad terms
|
|
63
|
+
|
|
64
|
+
Respond as JSON array of strings.`,
|
|
65
|
+
config: {
|
|
66
|
+
responseMimeType: "application/json",
|
|
67
|
+
responseSchema: {
|
|
68
|
+
type: "array",
|
|
69
|
+
items: { type: "string" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return JSON.parse(response.text ?? "[]");
|
|
74
|
+
}
|
|
75
|
+
async function suggest(options) {
|
|
76
|
+
const dataSrc = resolve(options?.dataSrc ?? "store/data");
|
|
77
|
+
const exportsDir = resolve(options?.exportsDir ?? "store/exports");
|
|
78
|
+
const { storeData } = await createJiti(import.meta.url).import(dataSrc);
|
|
79
|
+
const { GoogleGenAI } = await import("@google/genai");
|
|
80
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
81
|
+
if (!apiKey) {
|
|
82
|
+
console.error("GEMINI_API_KEY env var is required");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const ai = new GoogleGenAI({ apiKey });
|
|
86
|
+
const model = "gemini-2.5-flash";
|
|
87
|
+
const permissions = Object.keys(storeData.chrome.permissionJustifications);
|
|
88
|
+
const { description } = storeData.common.locales.en;
|
|
89
|
+
const pause = () => new Promise((res) => setTimeout(res, 1e3));
|
|
90
|
+
const permissionJustifications = await generatePermissionJustifications(ai, model, permissions, description);
|
|
91
|
+
await pause();
|
|
92
|
+
const singlePurpose = await generateSinglePurpose(ai, model, description);
|
|
93
|
+
await pause();
|
|
94
|
+
const searchTerms = await generateSearchTerms(ai, model, description);
|
|
95
|
+
const resultJson = JSON.stringify({
|
|
96
|
+
permissionJustifications,
|
|
97
|
+
singlePurpose,
|
|
98
|
+
searchTerms
|
|
99
|
+
}, null, 4);
|
|
100
|
+
await mkdir(exportsDir, { recursive: true });
|
|
101
|
+
await writeFile(resolve(exportsDir, "suggested-data.json"), resultJson, "utf-8");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
export { suggest as t };
|
package/package.json
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernocal/viteshot",
|
|
3
3
|
"description": "fork adding weird popup sidebar options screenshots. original by @aklinker1",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"async-mutex": "^0.5.0",
|
|
8
|
+
"jiti": "^2.4.2",
|
|
8
9
|
"natural-compare-lite": "^1.4.0",
|
|
9
10
|
"p-map": "^7.0.4",
|
|
10
11
|
"p-queue": "^9.1.0",
|
|
11
12
|
"puppeteer-core": "^24.37.5"
|
|
12
13
|
},
|
|
13
14
|
"peerDependencies": {
|
|
15
|
+
"@google/genai": ">=1",
|
|
16
|
+
"@kernocal/validate-store": ">=0.1.8",
|
|
14
17
|
"svelte": ">=5",
|
|
15
18
|
"vite": ">=5"
|
|
16
19
|
},
|
|
17
20
|
"peerDependenciesMeta": {
|
|
21
|
+
"@google/genai": {
|
|
22
|
+
"optional": true
|
|
23
|
+
},
|
|
24
|
+
"@kernocal/validate-store": {
|
|
25
|
+
"optional": true
|
|
26
|
+
},
|
|
18
27
|
"svelte": {
|
|
19
28
|
"optional": true
|
|
20
29
|
}
|