@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,137 @@
1
+ // setup.js - Consolidated command: auth + init + studio launch
2
+ const chalk = require("chalk");
3
+ const oraModule = require("ora");
4
+ const ora = oraModule.default || oraModule;
5
+
6
+ const {
7
+ writeSettings,
8
+ readSettings,
9
+ configExists,
10
+ writeConfig,
11
+ SETTINGS_DIR,
12
+ } = require("../lib/config");
13
+
14
+ /**
15
+ * Create a default docsync.config.json for platform mode
16
+ * @param {Object} settings - Project settings from auth
17
+ * @returns {Object} The created config
18
+ */
19
+ function createDefaultConfig(settings) {
20
+ const defaultConfig = {
21
+ $schema: "https://reshot.dev/schemas/docsync-config.json",
22
+ version: "2.0",
23
+ projectId: settings.projectId,
24
+ baseUrl: "http://localhost:3000",
25
+ assetDir: ".reshot/output",
26
+ viewport: { width: 1280, height: 720 },
27
+ timeout: 30000,
28
+ headless: true,
29
+ scenarios: [],
30
+ };
31
+
32
+ return defaultConfig;
33
+ }
34
+
35
+ /**
36
+ * Main setup command - consolidates auth, init, and launches studio
37
+ * One command to rule them all!
38
+ *
39
+ * @param {Object} options - Command options
40
+ * @param {boolean} options.noStudio - Skip launching studio after setup
41
+ * @param {boolean} options.force - Force re-initialization even if already set up
42
+ */
43
+ async function setupCommand(options = {}) {
44
+ const { noStudio = false, force = false } = options;
45
+
46
+ console.log(chalk.cyan("\nšŸš€ Reshot Setup\n"));
47
+ console.log(chalk.gray("Setting up your project in one step...\n"));
48
+
49
+ // Step 1: Check current authentication state
50
+ let existingSettings = null;
51
+ let isAlreadyAuthed = false;
52
+
53
+ try {
54
+ existingSettings = readSettings();
55
+ isAlreadyAuthed = !!(
56
+ existingSettings?.apiKey && existingSettings?.projectId
57
+ );
58
+ } catch (e) {
59
+ // No existing settings - that's fine
60
+ }
61
+
62
+ // Step 2: Handle authentication
63
+ if (isAlreadyAuthed && !force) {
64
+ console.log(
65
+ chalk.green("āœ” Already authenticated as:"),
66
+ chalk.cyan(existingSettings.projectName || existingSettings.projectId)
67
+ );
68
+ console.log(
69
+ chalk.gray(
70
+ " Use --force to re-authenticate with a different project.\n"
71
+ )
72
+ );
73
+ } else {
74
+ // Run browser-based authentication
75
+ console.log(chalk.gray("Opening browser for authentication...\n"));
76
+
77
+ const authCommand = require("./auth");
78
+ await authCommand();
79
+
80
+ // Re-read settings after auth
81
+ try {
82
+ existingSettings = readSettings();
83
+ isAlreadyAuthed = !!(
84
+ existingSettings?.apiKey && existingSettings?.projectId
85
+ );
86
+ } catch (e) {
87
+ throw new Error("Authentication failed. Please try again.");
88
+ }
89
+
90
+ if (!isAlreadyAuthed) {
91
+ throw new Error("Authentication was not completed.");
92
+ }
93
+
94
+ console.log(); // Add spacing after auth
95
+ }
96
+
97
+ // Step 3: Handle initialization (create docsync.config.json if missing)
98
+ const hasConfig = configExists();
99
+
100
+ if (hasConfig && !force) {
101
+ console.log(chalk.green("āœ” Configuration found:"), chalk.cyan("docsync.config.json"));
102
+ } else if (hasConfig && force) {
103
+ console.log(chalk.yellow("⚠ Overwriting existing docsync.config.json..."));
104
+ const newConfig = createDefaultConfig(existingSettings);
105
+ writeConfig(newConfig);
106
+ console.log(chalk.green("āœ” Configuration updated"));
107
+ } else {
108
+ // No config - create it
109
+ console.log(chalk.gray("Creating docsync.config.json..."));
110
+ const newConfig = createDefaultConfig(existingSettings);
111
+ writeConfig(newConfig);
112
+ console.log(chalk.green("āœ” Configuration created:"), chalk.cyan("docsync.config.json"));
113
+ }
114
+
115
+ // Step 4: Success summary
116
+ console.log(chalk.green("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
117
+ console.log(chalk.green.bold("āœ” Project initialized successfully!"));
118
+ console.log(chalk.green("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"));
119
+
120
+ // Step 5: Launch Studio (unless --no-studio)
121
+ if (!noStudio) {
122
+ console.log(chalk.cyan("šŸŽ¬ Launching Reshot Studio...\n"));
123
+
124
+ // Small delay to let the user read the success message
125
+ await new Promise((resolve) => setTimeout(resolve, 500));
126
+
127
+ const uiCommand = require("./ui");
128
+ await uiCommand({ open: true });
129
+ } else {
130
+ console.log(chalk.gray("Next steps:"));
131
+ console.log(chalk.gray(" • Run"), chalk.cyan("reshot studio"), chalk.gray("to open the management UI"));
132
+ console.log(chalk.gray(" • Run"), chalk.cyan("reshot record"), chalk.gray("to capture your first visual"));
133
+ console.log(chalk.gray(" • Run"), chalk.cyan("reshot run"), chalk.gray("to execute scenarios\n"));
134
+ }
135
+ }
136
+
137
+ module.exports = setupCommand;
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * status - Check DocSync status, sync jobs, and drift queue
4
+ *
5
+ * Displays:
6
+ * - Current project configuration
7
+ * - Recent sync jobs with status
8
+ * - Drift queue summary
9
+ * - Pending actions
10
+ *
11
+ * Usage: reshot status [--jobs] [--drifts] [--json]
12
+ */
13
+
14
+ const chalk = require("chalk");
15
+ const config = require("../lib/config");
16
+ const apiClient = require("../lib/api-client");
17
+
18
+ /**
19
+ * Format timestamp for display
20
+ */
21
+ function formatTime(dateString) {
22
+ const date = new Date(dateString);
23
+ const now = new Date();
24
+ const diffMs = now - date;
25
+ const diffMins = Math.floor(diffMs / 60000);
26
+ const diffHours = Math.floor(diffMs / 3600000);
27
+ const diffDays = Math.floor(diffMs / 86400000);
28
+
29
+ if (diffMins < 1) return "just now";
30
+ if (diffMins < 60) return `${diffMins}m ago`;
31
+ if (diffHours < 24) return `${diffHours}h ago`;
32
+ if (diffDays < 7) return `${diffDays}d ago`;
33
+ return date.toLocaleDateString();
34
+ }
35
+
36
+ /**
37
+ * Format job status with color
38
+ */
39
+ function formatJobStatus(status) {
40
+ switch (status) {
41
+ case "COMPLETED":
42
+ return chalk.green("āœ“ Completed");
43
+ case "PROCESSING":
44
+ return chalk.blue("⟳ Processing");
45
+ case "QUEUED":
46
+ return chalk.yellow("ā—· Queued");
47
+ case "FAILED":
48
+ return chalk.red("āœ— Failed");
49
+ default:
50
+ return chalk.gray(status);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Format drift status with color
56
+ */
57
+ function formatDriftStatus(status) {
58
+ switch (status) {
59
+ case "PENDING":
60
+ return chalk.yellow("ā— Pending");
61
+ case "APPROVED":
62
+ return chalk.green("āœ“ Approved");
63
+ case "REJECTED":
64
+ return chalk.red("āœ— Rejected");
65
+ case "IGNORED":
66
+ return chalk.gray("ā—‹ Ignored");
67
+ default:
68
+ return chalk.gray(status);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Format drift type with color
74
+ */
75
+ function formatDriftType(type) {
76
+ switch (type) {
77
+ case "VISUAL":
78
+ return chalk.cyan("šŸ–¼ Visual");
79
+ case "SEMANTIC":
80
+ return chalk.magenta("šŸ“ Text");
81
+ case "BOTH":
82
+ return chalk.yellow("⚔ Both");
83
+ default:
84
+ return chalk.gray(type);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Get and display project configuration
90
+ */
91
+ async function displayConfig(docSyncConfig) {
92
+ console.log(chalk.bold("\nšŸ“‹ Project Configuration\n"));
93
+
94
+ const docConfig = docSyncConfig.documentation;
95
+ if (!docConfig) {
96
+ console.log(chalk.yellow(" No documentation block configured."));
97
+ console.log(chalk.gray(" Add a 'documentation' block to docsync.config.json"));
98
+ return;
99
+ }
100
+
101
+ console.log(chalk.gray(" Root: ") + chalk.white(docConfig.root || "./docs"));
102
+ console.log(chalk.gray(" Strategy: ") + chalk.white(docConfig.strategy));
103
+ console.log(chalk.gray(" Format: ") + chalk.white(docConfig.assetFormat || "markdown"));
104
+
105
+ if (docConfig.include?.length) {
106
+ console.log(chalk.gray(" Include: ") + chalk.white(docConfig.include.join(", ")));
107
+ }
108
+ if (docConfig.exclude?.length) {
109
+ console.log(chalk.gray(" Exclude: ") + chalk.white(docConfig.exclude.join(", ")));
110
+ }
111
+
112
+ const mappingCount = Object.keys(docConfig.mappings || {}).length;
113
+ if (mappingCount > 0) {
114
+ console.log(chalk.gray(" Mappings: ") + chalk.white(`${mappingCount} explicit binding(s)`));
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Get and display sync job history
120
+ */
121
+ async function displayJobs(apiKey, projectId, limit = 5) {
122
+ console.log(chalk.bold("\nšŸ“¦ Recent Sync Jobs\n"));
123
+
124
+ try {
125
+ // Fetch jobs via API
126
+ const response = await apiClient.getSyncJobs(apiKey, projectId, { limit });
127
+
128
+ const jobs = response.jobs || response.data?.jobs || [];
129
+
130
+ if (jobs.length === 0) {
131
+ console.log(chalk.gray(" No sync jobs found."));
132
+ console.log(chalk.gray(" Run `reshot ingest` to upload traces and docs."));
133
+ return;
134
+ }
135
+
136
+ for (const job of jobs) {
137
+ console.log(
138
+ ` ${formatJobStatus(job.status)} ` +
139
+ chalk.gray(`${job.id.slice(0, 8)} `) +
140
+ chalk.white(`${job.branch || "unknown"}@${(job.commitHash || "").slice(0, 7)} `) +
141
+ chalk.gray(formatTime(job.createdAt))
142
+ );
143
+
144
+ if (job.driftCount > 0) {
145
+ console.log(chalk.gray(` └─ ${job.driftCount} drift(s) detected`));
146
+ }
147
+ if (job.errorMessage) {
148
+ console.log(chalk.red(` └─ Error: ${job.errorMessage}`));
149
+ }
150
+ }
151
+ } catch (error) {
152
+ console.log(chalk.yellow(" Could not fetch sync jobs: " + error.message));
153
+ console.log(chalk.gray(" This endpoint may not be available yet."));
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Get and display drift queue
159
+ */
160
+ async function displayDrifts(apiKey, projectId, limit = 10) {
161
+ console.log(chalk.bold("\nšŸ”„ Drift Queue\n"));
162
+
163
+ try {
164
+ const response = await apiClient.getDrifts(apiKey, projectId, { status: "PENDING" });
165
+ const drifts = response.drifts || [];
166
+ const stats = response.stats || {};
167
+
168
+ // Show stats summary
169
+ if (stats.total > 0) {
170
+ console.log(
171
+ chalk.gray(" Summary: ") +
172
+ chalk.yellow(`${stats.pending || 0} pending`) + ", " +
173
+ chalk.green(`${stats.approved || 0} approved`) + ", " +
174
+ chalk.red(`${stats.rejected || 0} rejected`) + ", " +
175
+ chalk.gray(`${stats.ignored || 0} ignored`)
176
+ );
177
+ console.log();
178
+ }
179
+
180
+ if (drifts.length === 0) {
181
+ console.log(chalk.green(" āœ“ No pending drifts!"));
182
+ console.log(chalk.gray(" Your documentation is in sync with the application."));
183
+ return;
184
+ }
185
+
186
+ // Show pending drifts
187
+ const displayed = drifts.slice(0, limit);
188
+ for (const drift of displayed) {
189
+ console.log(
190
+ ` ${formatDriftType(drift.driftType)} ` +
191
+ chalk.white(drift.journeyKey || drift.docPath) + " " +
192
+ chalk.gray(`(${Math.round(drift.confidenceScore * 100)}% confidence)`)
193
+ );
194
+
195
+ if (drift.docPath) {
196
+ console.log(chalk.gray(` └─ ${drift.docPath}`));
197
+ }
198
+ }
199
+
200
+ if (drifts.length > limit) {
201
+ console.log(chalk.gray(`\n ... and ${drifts.length - limit} more.`));
202
+ }
203
+
204
+ console.log();
205
+ console.log(chalk.gray(" View in dashboard: ") + chalk.blue(`https://reshot.dev/app/projects/${projectId}/docsync`));
206
+ } catch (error) {
207
+ console.log(chalk.yellow(" Could not fetch drifts: " + error.message));
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Main status command
213
+ */
214
+ async function statusCommand(options = {}) {
215
+ console.log(chalk.blue("\nšŸ“Š Reshot DocSync Status\n"));
216
+
217
+ // Read configuration
218
+ let docSyncConfig;
219
+ try {
220
+ docSyncConfig = config.readDocSyncConfig();
221
+ } catch (error) {
222
+ console.error(chalk.red("Error:"), "docsync.config.json not found. Run `reshot init` first.");
223
+ process.exit(1);
224
+ }
225
+
226
+ // Get API key and project ID
227
+ const settings = config.readSettings();
228
+ const apiKey = process.env.RESHOT_API_KEY || settings?.apiKey;
229
+ const projectId =
230
+ process.env.RESHOT_PROJECT_ID ||
231
+ settings?.projectId ||
232
+ docSyncConfig._metadata?.projectId;
233
+
234
+ if (!apiKey) {
235
+ console.error(chalk.red("Error:"), "API key not found. Set RESHOT_API_KEY or run `reshot auth`.");
236
+ process.exit(1);
237
+ }
238
+
239
+ if (!projectId) {
240
+ console.error(chalk.red("Error:"), "Project ID not found. Set RESHOT_PROJECT_ID or run `reshot init`.");
241
+ process.exit(1);
242
+ }
243
+
244
+ console.log(chalk.gray(` Project: ${projectId}`));
245
+
246
+ // Display configuration
247
+ if (options.config || (!options.jobs && !options.drifts)) {
248
+ await displayConfig(docSyncConfig);
249
+ }
250
+
251
+ // Display jobs
252
+ if (options.jobs || (!options.config && !options.drifts)) {
253
+ await displayJobs(apiKey, projectId, options.limit || 5);
254
+ }
255
+
256
+ // Display drifts
257
+ if (options.drifts || (!options.config && !options.jobs)) {
258
+ await displayDrifts(apiKey, projectId, options.limit || 10);
259
+ }
260
+
261
+ // JSON output mode
262
+ if (options.json) {
263
+ const response = await apiClient.getDrifts(apiKey, projectId).catch(() => ({}));
264
+ console.log(JSON.stringify({
265
+ projectId,
266
+ config: docSyncConfig.documentation,
267
+ drifts: response.drifts || [],
268
+ stats: response.stats || {},
269
+ }, null, 2));
270
+ }
271
+
272
+ console.log();
273
+ }
274
+
275
+ module.exports = statusCommand;