@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.
Files changed (59) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +388 -0
  3. package/package.json +64 -0
  4. package/src/commands/auth.js +259 -0
  5. package/src/commands/chrome.js +140 -0
  6. package/src/commands/ci-run.js +123 -0
  7. package/src/commands/ci-setup.js +288 -0
  8. package/src/commands/drifts.js +423 -0
  9. package/src/commands/import-tests.js +309 -0
  10. package/src/commands/ingest.js +458 -0
  11. package/src/commands/init.js +633 -0
  12. package/src/commands/publish.js +1721 -0
  13. package/src/commands/pull.js +303 -0
  14. package/src/commands/record.js +94 -0
  15. package/src/commands/run.js +476 -0
  16. package/src/commands/setup-wizard.js +740 -0
  17. package/src/commands/setup.js +137 -0
  18. package/src/commands/status.js +275 -0
  19. package/src/commands/sync.js +621 -0
  20. package/src/commands/ui.js +248 -0
  21. package/src/commands/validate-docs.js +529 -0
  22. package/src/index.js +462 -0
  23. package/src/lib/api-client.js +815 -0
  24. package/src/lib/capture-engine.js +1623 -0
  25. package/src/lib/capture-script-runner.js +3120 -0
  26. package/src/lib/ci-detect.js +137 -0
  27. package/src/lib/config.js +1240 -0
  28. package/src/lib/diff-engine.js +642 -0
  29. package/src/lib/hash.js +74 -0
  30. package/src/lib/image-crop.js +396 -0
  31. package/src/lib/matrix.js +89 -0
  32. package/src/lib/output-path-template.js +318 -0
  33. package/src/lib/playwright-runner.js +252 -0
  34. package/src/lib/polished-clip.js +553 -0
  35. package/src/lib/privacy-engine.js +408 -0
  36. package/src/lib/progress-tracker.js +142 -0
  37. package/src/lib/record-browser-injection.js +654 -0
  38. package/src/lib/record-cdp.js +612 -0
  39. package/src/lib/record-clip.js +343 -0
  40. package/src/lib/record-config.js +623 -0
  41. package/src/lib/record-screenshot.js +360 -0
  42. package/src/lib/record-terminal.js +123 -0
  43. package/src/lib/recorder-service.js +781 -0
  44. package/src/lib/secrets.js +51 -0
  45. package/src/lib/selector-strategies.js +859 -0
  46. package/src/lib/standalone-mode.js +400 -0
  47. package/src/lib/storage-providers.js +569 -0
  48. package/src/lib/style-engine.js +684 -0
  49. package/src/lib/ui-api.js +4677 -0
  50. package/src/lib/ui-assets.js +373 -0
  51. package/src/lib/ui-executor.js +587 -0
  52. package/src/lib/variant-injector.js +591 -0
  53. package/src/lib/viewport-presets.js +454 -0
  54. package/src/lib/worker-pool.js +118 -0
  55. package/web/cropper/index.html +436 -0
  56. package/web/manager/dist/assets/index--ZgioErz.js +507 -0
  57. package/web/manager/dist/assets/index-n468W0Wr.css +1 -0
  58. package/web/manager/dist/index.html +27 -0
  59. 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
+ };