@reshotdev/screenshot 0.0.1-beta.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/LICENSE +190 -0
- package/README.md +388 -0
- package/package.json +64 -0
- package/src/commands/auth.js +259 -0
- package/src/commands/chrome.js +140 -0
- package/src/commands/ci-run.js +123 -0
- package/src/commands/ci-setup.js +288 -0
- package/src/commands/drifts.js +423 -0
- package/src/commands/import-tests.js +309 -0
- package/src/commands/ingest.js +458 -0
- package/src/commands/init.js +633 -0
- package/src/commands/publish.js +1721 -0
- package/src/commands/pull.js +303 -0
- package/src/commands/record.js +94 -0
- package/src/commands/run.js +476 -0
- package/src/commands/setup-wizard.js +740 -0
- package/src/commands/setup.js +137 -0
- package/src/commands/status.js +275 -0
- package/src/commands/sync.js +621 -0
- package/src/commands/ui.js +248 -0
- package/src/commands/validate-docs.js +529 -0
- package/src/index.js +462 -0
- package/src/lib/api-client.js +815 -0
- package/src/lib/capture-engine.js +1623 -0
- package/src/lib/capture-script-runner.js +3120 -0
- package/src/lib/ci-detect.js +137 -0
- package/src/lib/config.js +1240 -0
- package/src/lib/diff-engine.js +642 -0
- package/src/lib/hash.js +74 -0
- package/src/lib/image-crop.js +396 -0
- package/src/lib/matrix.js +89 -0
- package/src/lib/output-path-template.js +318 -0
- package/src/lib/playwright-runner.js +252 -0
- package/src/lib/polished-clip.js +553 -0
- package/src/lib/privacy-engine.js +408 -0
- package/src/lib/progress-tracker.js +142 -0
- package/src/lib/record-browser-injection.js +654 -0
- package/src/lib/record-cdp.js +612 -0
- package/src/lib/record-clip.js +343 -0
- package/src/lib/record-config.js +623 -0
- package/src/lib/record-screenshot.js +360 -0
- package/src/lib/record-terminal.js +123 -0
- package/src/lib/recorder-service.js +781 -0
- package/src/lib/secrets.js +51 -0
- package/src/lib/selector-strategies.js +859 -0
- package/src/lib/standalone-mode.js +400 -0
- package/src/lib/storage-providers.js +569 -0
- package/src/lib/style-engine.js +684 -0
- package/src/lib/ui-api.js +4677 -0
- package/src/lib/ui-assets.js +373 -0
- package/src/lib/ui-executor.js +587 -0
- package/src/lib/variant-injector.js +591 -0
- package/src/lib/viewport-presets.js +454 -0
- package/src/lib/worker-pool.js +118 -0
- package/web/cropper/index.html +436 -0
- package/web/manager/dist/assets/index--ZgioErz.js +507 -0
- package/web/manager/dist/assets/index-n468W0Wr.css +1 -0
- package/web/manager/dist/index.html +27 -0
- package/web/subtitle-editor/index.html +295 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
// standalone-mode.js - Standalone mode support for Reshot CLI
|
|
2
|
+
// Allows CLI to work without connecting to Reshot platform
|
|
3
|
+
|
|
4
|
+
const fs = require("fs-extra");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const chalk = require("chalk");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Default standalone configuration
|
|
10
|
+
* Used when no platform connection is available
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_STANDALONE_CONFIG = {
|
|
13
|
+
baseUrl: "http://localhost:3000",
|
|
14
|
+
assetDir: ".reshot/output",
|
|
15
|
+
concurrency: 1,
|
|
16
|
+
defaultWaitUntil: "networkidle",
|
|
17
|
+
viewport: { width: 1280, height: 720 },
|
|
18
|
+
timeout: 45000,
|
|
19
|
+
headless: true,
|
|
20
|
+
|
|
21
|
+
// Default output template
|
|
22
|
+
output: {
|
|
23
|
+
template: ".reshot/output/{{scenario}}/{{timestamp}}/{{variant}}/{{name}}.{{ext}}",
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// Default variant dimensions (common examples)
|
|
27
|
+
variants: {
|
|
28
|
+
dimensions: {
|
|
29
|
+
locale: {
|
|
30
|
+
label: "Language",
|
|
31
|
+
description: "UI language for the application",
|
|
32
|
+
options: {
|
|
33
|
+
en: {
|
|
34
|
+
name: "English",
|
|
35
|
+
inject: [
|
|
36
|
+
{ method: "localStorage", key: "locale", value: "en" },
|
|
37
|
+
{ method: "browser", locale: "en-US", timezone: "America/New_York" },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
theme: {
|
|
43
|
+
label: "Theme",
|
|
44
|
+
description: "Light or dark mode",
|
|
45
|
+
options: {
|
|
46
|
+
light: {
|
|
47
|
+
name: "Light Mode",
|
|
48
|
+
inject: [
|
|
49
|
+
{ method: "localStorage", key: "theme", value: "light" },
|
|
50
|
+
{ method: "browser", colorScheme: "light" },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
dark: {
|
|
54
|
+
name: "Dark Mode",
|
|
55
|
+
inject: [
|
|
56
|
+
{ method: "localStorage", key: "theme", value: "dark" },
|
|
57
|
+
{ method: "browser", colorScheme: "dark" },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
presets: {},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Empty scenarios - user will create their own
|
|
67
|
+
scenarios: [],
|
|
68
|
+
|
|
69
|
+
// Standalone metadata
|
|
70
|
+
_metadata: {
|
|
71
|
+
projectId: null,
|
|
72
|
+
projectName: "Standalone Project",
|
|
73
|
+
generatedAt: null,
|
|
74
|
+
mode: "standalone",
|
|
75
|
+
visualCount: 0,
|
|
76
|
+
contextCount: 1,
|
|
77
|
+
features: {
|
|
78
|
+
visuals: true,
|
|
79
|
+
docs: false,
|
|
80
|
+
changelog: false,
|
|
81
|
+
platformSync: false,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Standalone mode settings structure
|
|
88
|
+
*/
|
|
89
|
+
const DEFAULT_STANDALONE_SETTINGS = {
|
|
90
|
+
mode: "standalone",
|
|
91
|
+
apiKey: null,
|
|
92
|
+
projectId: null,
|
|
93
|
+
projectName: "Standalone Project",
|
|
94
|
+
platformUrl: null,
|
|
95
|
+
lastSyncedAt: null,
|
|
96
|
+
features: {
|
|
97
|
+
offlineCapture: true,
|
|
98
|
+
localDiffing: true,
|
|
99
|
+
outputTemplating: true,
|
|
100
|
+
viewportPresets: true,
|
|
101
|
+
matrixCapture: true,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const SETTINGS_DIR = ".reshot";
|
|
106
|
+
const CONFIG_PATH = "docsync.config.json";
|
|
107
|
+
const SETTINGS_PATH = path.join(SETTINGS_DIR, "settings.json");
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if CLI is running in standalone mode
|
|
111
|
+
* Standalone mode is active when:
|
|
112
|
+
* - No API key is configured
|
|
113
|
+
* - Settings explicitly set mode: "standalone"
|
|
114
|
+
* - Platform sync is disabled in features
|
|
115
|
+
*
|
|
116
|
+
* @param {Object} [settings] - Optional settings object (will be read if not provided)
|
|
117
|
+
* @returns {boolean}
|
|
118
|
+
*/
|
|
119
|
+
function isStandaloneMode(settings = null) {
|
|
120
|
+
if (settings) {
|
|
121
|
+
return settings.mode === "standalone" || !settings.apiKey;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const settingsPath = path.join(process.cwd(), SETTINGS_PATH);
|
|
126
|
+
if (!fs.existsSync(settingsPath)) {
|
|
127
|
+
return true; // No settings = standalone
|
|
128
|
+
}
|
|
129
|
+
const loadedSettings = fs.readJSONSync(settingsPath);
|
|
130
|
+
return loadedSettings.mode === "standalone" || !loadedSettings.apiKey;
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return true; // Error reading settings = standalone
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if platform features are available
|
|
138
|
+
* @param {Object} [settings] - Optional settings object
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
function isPlatformConnected(settings = null) {
|
|
142
|
+
return !isStandaloneMode(settings);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get features available in current mode
|
|
147
|
+
* @param {Object} [settings] - Optional settings object
|
|
148
|
+
* @returns {Object} Available features
|
|
149
|
+
*/
|
|
150
|
+
function getAvailableFeatures(settings = null) {
|
|
151
|
+
const standalone = isStandaloneMode(settings);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
// Always available
|
|
155
|
+
capture: true,
|
|
156
|
+
viewportPresets: true,
|
|
157
|
+
outputTemplating: true,
|
|
158
|
+
matrixCapture: true,
|
|
159
|
+
localDiffing: true,
|
|
160
|
+
recording: true,
|
|
161
|
+
|
|
162
|
+
// Platform-dependent
|
|
163
|
+
platformSync: !standalone,
|
|
164
|
+
cloudBaselines: !standalone,
|
|
165
|
+
publishing: !standalone,
|
|
166
|
+
changelog: !standalone,
|
|
167
|
+
teamCollaboration: !standalone,
|
|
168
|
+
analytics: !standalone,
|
|
169
|
+
|
|
170
|
+
// Mode info
|
|
171
|
+
mode: standalone ? "standalone" : "connected",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Initialize standalone mode configuration
|
|
177
|
+
* Creates default config files if they don't exist
|
|
178
|
+
*
|
|
179
|
+
* @param {Object} options - Initialization options
|
|
180
|
+
* @param {string} options.projectName - Optional project name
|
|
181
|
+
* @param {boolean} options.force - Force overwrite existing config
|
|
182
|
+
* @returns {Object} Initialized configuration
|
|
183
|
+
*/
|
|
184
|
+
function initStandaloneMode(options = {}) {
|
|
185
|
+
const { projectName, force = false } = options;
|
|
186
|
+
const cwd = process.cwd();
|
|
187
|
+
|
|
188
|
+
const configPath = path.join(cwd, CONFIG_PATH);
|
|
189
|
+
const settingsPath = path.join(cwd, SETTINGS_PATH);
|
|
190
|
+
|
|
191
|
+
// Check if already initialized
|
|
192
|
+
if (!force && fs.existsSync(configPath)) {
|
|
193
|
+
console.log(chalk.yellow("⚠ Config already exists. Use --force to overwrite."));
|
|
194
|
+
return fs.readJSONSync(configPath);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Create settings directory
|
|
198
|
+
fs.ensureDirSync(path.join(cwd, SETTINGS_DIR));
|
|
199
|
+
|
|
200
|
+
// Create standalone settings
|
|
201
|
+
const settings = {
|
|
202
|
+
...DEFAULT_STANDALONE_SETTINGS,
|
|
203
|
+
projectName: projectName || path.basename(cwd),
|
|
204
|
+
};
|
|
205
|
+
fs.writeJSONSync(settingsPath, settings, { spaces: 2 });
|
|
206
|
+
|
|
207
|
+
// Create standalone config
|
|
208
|
+
const config = {
|
|
209
|
+
...DEFAULT_STANDALONE_CONFIG,
|
|
210
|
+
_metadata: {
|
|
211
|
+
...DEFAULT_STANDALONE_CONFIG._metadata,
|
|
212
|
+
projectName: projectName || path.basename(cwd),
|
|
213
|
+
generatedAt: new Date().toISOString(),
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
fs.writeJSONSync(configPath, config, { spaces: 2 });
|
|
217
|
+
|
|
218
|
+
console.log(chalk.green("✔ Initialized Reshot in standalone mode"));
|
|
219
|
+
console.log(chalk.gray(` Config: ${configPath}`));
|
|
220
|
+
console.log(chalk.gray(` Settings: ${settingsPath}`));
|
|
221
|
+
|
|
222
|
+
return config;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Convert standalone config to connected mode
|
|
227
|
+
* Called when user authenticates
|
|
228
|
+
*
|
|
229
|
+
* @param {Object} platformConfig - Config received from platform
|
|
230
|
+
* @param {Object} credentials - { apiKey, projectId, projectName }
|
|
231
|
+
* @returns {Object} Merged configuration
|
|
232
|
+
*/
|
|
233
|
+
function upgradeToConnectedMode(platformConfig, credentials) {
|
|
234
|
+
const cwd = process.cwd();
|
|
235
|
+
const configPath = path.join(cwd, CONFIG_PATH);
|
|
236
|
+
const settingsPath = path.join(cwd, SETTINGS_PATH);
|
|
237
|
+
|
|
238
|
+
// Read existing local config
|
|
239
|
+
let localConfig = {};
|
|
240
|
+
if (fs.existsSync(configPath)) {
|
|
241
|
+
try {
|
|
242
|
+
localConfig = fs.readJSONSync(configPath);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
// Ignore parse errors
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Merge configs - platform config takes priority but preserve local scenarios
|
|
249
|
+
const mergedConfig = {
|
|
250
|
+
...platformConfig,
|
|
251
|
+
// Preserve local viewport presets if any
|
|
252
|
+
viewportPresets: {
|
|
253
|
+
...platformConfig.viewportPresets,
|
|
254
|
+
...localConfig.viewportPresets,
|
|
255
|
+
},
|
|
256
|
+
// Preserve local output template if set
|
|
257
|
+
output: localConfig.output || platformConfig.output,
|
|
258
|
+
// Merge scenarios - local scenarios not in platform are preserved
|
|
259
|
+
scenarios: mergeScenarios(platformConfig.scenarios || [], localConfig.scenarios || []),
|
|
260
|
+
_metadata: {
|
|
261
|
+
...platformConfig._metadata,
|
|
262
|
+
mode: "connected",
|
|
263
|
+
mergedAt: new Date().toISOString(),
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Update settings
|
|
268
|
+
const settings = {
|
|
269
|
+
...DEFAULT_STANDALONE_SETTINGS,
|
|
270
|
+
mode: "connected",
|
|
271
|
+
apiKey: credentials.apiKey,
|
|
272
|
+
projectId: credentials.projectId,
|
|
273
|
+
projectName: credentials.projectName,
|
|
274
|
+
lastSyncedAt: new Date().toISOString(),
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
fs.writeJSONSync(settingsPath, settings, { spaces: 2 });
|
|
278
|
+
fs.writeJSONSync(configPath, mergedConfig, { spaces: 2 });
|
|
279
|
+
|
|
280
|
+
console.log(chalk.green("✔ Upgraded to connected mode"));
|
|
281
|
+
|
|
282
|
+
return mergedConfig;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Merge scenarios from platform and local
|
|
287
|
+
* Platform scenarios take priority, but local-only scenarios are preserved
|
|
288
|
+
*
|
|
289
|
+
* @param {Array} platformScenarios - Scenarios from platform
|
|
290
|
+
* @param {Array} localScenarios - Local scenarios
|
|
291
|
+
* @returns {Array} Merged scenarios
|
|
292
|
+
*/
|
|
293
|
+
function mergeScenarios(platformScenarios, localScenarios) {
|
|
294
|
+
const platformKeys = new Set(platformScenarios.map(s => s.key));
|
|
295
|
+
|
|
296
|
+
// Start with platform scenarios
|
|
297
|
+
const merged = [...platformScenarios];
|
|
298
|
+
|
|
299
|
+
// Add local-only scenarios (not on platform)
|
|
300
|
+
for (const localScenario of localScenarios) {
|
|
301
|
+
if (!platformKeys.has(localScenario.key)) {
|
|
302
|
+
merged.push({
|
|
303
|
+
...localScenario,
|
|
304
|
+
_localOnly: true, // Mark as local-only
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return merged;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get appropriate config defaults based on mode
|
|
314
|
+
* @returns {Object} Configuration defaults
|
|
315
|
+
*/
|
|
316
|
+
function getConfigDefaults() {
|
|
317
|
+
return { ...DEFAULT_STANDALONE_CONFIG };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Validate that minimum requirements are met for capture
|
|
322
|
+
* Works in both standalone and connected modes
|
|
323
|
+
*
|
|
324
|
+
* @param {Object} config - Configuration to validate
|
|
325
|
+
* @returns {{ valid: boolean, errors: string[], warnings: string[] }}
|
|
326
|
+
*/
|
|
327
|
+
function validateCaptureRequirements(config) {
|
|
328
|
+
const errors = [];
|
|
329
|
+
const warnings = [];
|
|
330
|
+
|
|
331
|
+
if (!config) {
|
|
332
|
+
errors.push("No configuration found. Run `reshot init` first.");
|
|
333
|
+
return { valid: false, errors, warnings };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Check baseUrl
|
|
337
|
+
if (!config.baseUrl) {
|
|
338
|
+
errors.push("baseUrl is required. Set the URL of your application.");
|
|
339
|
+
} else if (!config.baseUrl.startsWith("http")) {
|
|
340
|
+
warnings.push("baseUrl should start with http:// or https://");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Check scenarios
|
|
344
|
+
if (!config.scenarios || config.scenarios.length === 0) {
|
|
345
|
+
warnings.push("No scenarios defined. Use the recorder to create scenarios.");
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check viewport
|
|
349
|
+
if (config.viewport) {
|
|
350
|
+
if (!config.viewport.width || !config.viewport.height) {
|
|
351
|
+
errors.push("viewport.width and viewport.height are required");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
valid: errors.length === 0,
|
|
357
|
+
errors,
|
|
358
|
+
warnings,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Print mode status banner
|
|
364
|
+
* Shows current mode and available features
|
|
365
|
+
*/
|
|
366
|
+
function printModeStatus() {
|
|
367
|
+
const features = getAvailableFeatures();
|
|
368
|
+
|
|
369
|
+
if (features.mode === "standalone") {
|
|
370
|
+
console.log(chalk.cyan("\n┌─────────────────────────────────────────┐"));
|
|
371
|
+
console.log(chalk.cyan("│") + chalk.bold(" 📦 Reshot CLI - Standalone Mode ") + chalk.cyan("│"));
|
|
372
|
+
console.log(chalk.cyan("├─────────────────────────────────────────┤"));
|
|
373
|
+
console.log(chalk.cyan("│") + " ✔ Capture screenshots & videos " + chalk.cyan("│"));
|
|
374
|
+
console.log(chalk.cyan("│") + " ✔ Viewport presets & matrix capture " + chalk.cyan("│"));
|
|
375
|
+
console.log(chalk.cyan("│") + " ✔ Output path templating " + chalk.cyan("│"));
|
|
376
|
+
console.log(chalk.cyan("│") + " ✔ Local diffing " + chalk.cyan("│"));
|
|
377
|
+
console.log(chalk.cyan("│") + chalk.gray(" ○ Platform sync (auth required) ") + chalk.cyan("│"));
|
|
378
|
+
console.log(chalk.cyan("│") + chalk.gray(" ○ Cloud baselines (auth required) ") + chalk.cyan("│"));
|
|
379
|
+
console.log(chalk.cyan("└─────────────────────────────────────────┘\n"));
|
|
380
|
+
} else {
|
|
381
|
+
console.log(chalk.green("\n┌─────────────────────────────────────────┐"));
|
|
382
|
+
console.log(chalk.green("│") + chalk.bold(" 🔗 Reshot CLI - Connected Mode ") + chalk.green("│"));
|
|
383
|
+
console.log(chalk.green("│") + " All features enabled " + chalk.green("│"));
|
|
384
|
+
console.log(chalk.green("└─────────────────────────────────────────┘\n"));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
module.exports = {
|
|
389
|
+
DEFAULT_STANDALONE_CONFIG,
|
|
390
|
+
DEFAULT_STANDALONE_SETTINGS,
|
|
391
|
+
isStandaloneMode,
|
|
392
|
+
isPlatformConnected,
|
|
393
|
+
getAvailableFeatures,
|
|
394
|
+
initStandaloneMode,
|
|
395
|
+
upgradeToConnectedMode,
|
|
396
|
+
mergeScenarios,
|
|
397
|
+
getConfigDefaults,
|
|
398
|
+
validateCaptureRequirements,
|
|
399
|
+
printModeStatus,
|
|
400
|
+
};
|