@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,633 @@
|
|
|
1
|
+
const inquirer = require("inquirer");
|
|
2
|
+
const chalk = require("chalk");
|
|
3
|
+
const fs = require("fs-extra");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const config = require("../lib/config");
|
|
6
|
+
const {
|
|
7
|
+
validateStorageConfig,
|
|
8
|
+
getStorageSetupHelp,
|
|
9
|
+
isPlatformAvailable,
|
|
10
|
+
} = require("../lib/storage-providers");
|
|
11
|
+
const {
|
|
12
|
+
initStandaloneMode,
|
|
13
|
+
printModeStatus,
|
|
14
|
+
getConfigDefaults,
|
|
15
|
+
} = require("../lib/standalone-mode");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Auto-detect documentation directories
|
|
19
|
+
*/
|
|
20
|
+
function detectDocumentationRoot() {
|
|
21
|
+
const commonPaths = [
|
|
22
|
+
"docs",
|
|
23
|
+
"documentation",
|
|
24
|
+
"content",
|
|
25
|
+
"doc",
|
|
26
|
+
"guides",
|
|
27
|
+
".vitepress/content",
|
|
28
|
+
".docusaurus/docs",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
for (const dir of commonPaths) {
|
|
32
|
+
const fullPath = path.join(process.cwd(), dir);
|
|
33
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
34
|
+
// Check if it has markdown files
|
|
35
|
+
const files = fs.readdirSync(fullPath);
|
|
36
|
+
if (files.some((f) => f.endsWith(".md") || f.endsWith(".mdx"))) {
|
|
37
|
+
return dir;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate documentation block configuration
|
|
47
|
+
*/
|
|
48
|
+
function generateDocumentationConfig(root = "./docs", strategy = "git_pr") {
|
|
49
|
+
return {
|
|
50
|
+
root,
|
|
51
|
+
include: ["**/*.md", "**/*.mdx"],
|
|
52
|
+
exclude: ["**/_*.mdx", "**/node_modules/**", "**/.next/**"],
|
|
53
|
+
strategy,
|
|
54
|
+
assetFormat: "markdown",
|
|
55
|
+
mappings: {},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Scaffold a basic documentation structure
|
|
61
|
+
*/
|
|
62
|
+
async function scaffoldDocumentation() {
|
|
63
|
+
const docsDir = path.join(process.cwd(), "docs");
|
|
64
|
+
|
|
65
|
+
// Create docs directory
|
|
66
|
+
fs.ensureDirSync(docsDir);
|
|
67
|
+
|
|
68
|
+
// Create README
|
|
69
|
+
const readmeContent = `# Documentation
|
|
70
|
+
|
|
71
|
+
This directory contains documentation for your project.
|
|
72
|
+
|
|
73
|
+
## Getting Started
|
|
74
|
+
|
|
75
|
+
Documentation files are automatically synced with your codebase using Reshot DocSync.
|
|
76
|
+
|
|
77
|
+
### Linking Docs to Visuals
|
|
78
|
+
|
|
79
|
+
Add frontmatter to your markdown files to link them to visual journeys:
|
|
80
|
+
|
|
81
|
+
\`\`\`markdown
|
|
82
|
+
---
|
|
83
|
+
title: "Feature Name"
|
|
84
|
+
reshot_journey: "feature/workflow-name"
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
# Feature Documentation
|
|
88
|
+
|
|
89
|
+
Your content here...
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
## Commands
|
|
93
|
+
|
|
94
|
+
- \`reshot validate\` - Validate documentation configuration
|
|
95
|
+
- \`reshot ingest\` - Upload traces and documentation
|
|
96
|
+
- \`reshot drifts\` - View and manage documentation drifts
|
|
97
|
+
|
|
98
|
+
For more information, visit: https://docs.reshot.dev
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
fs.writeFileSync(path.join(docsDir, "README.md"), readmeContent);
|
|
102
|
+
|
|
103
|
+
// Create example doc
|
|
104
|
+
const exampleContent = `---
|
|
105
|
+
title: "Getting Started"
|
|
106
|
+
reshot_journey: "onboarding/first-steps"
|
|
107
|
+
description: "Quick start guide for new users"
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
# Getting Started
|
|
111
|
+
|
|
112
|
+
Welcome! This is an example documentation file.
|
|
113
|
+
|
|
114
|
+
## Steps
|
|
115
|
+
|
|
116
|
+
1. **Step 1**: Description of first step
|
|
117
|
+
2. **Step 2**: Description of second step
|
|
118
|
+
3. **Step 3**: Description of third step
|
|
119
|
+
|
|
120
|
+
## Next Steps
|
|
121
|
+
|
|
122
|
+
Continue to the next guide...
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
fs.writeFileSync(path.join(docsDir, "getting-started.md"), exampleContent);
|
|
126
|
+
|
|
127
|
+
return "docs";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function initCommand() {
|
|
131
|
+
console.log(chalk.cyan("š Initializing Reshot...\n"));
|
|
132
|
+
|
|
133
|
+
// Check if we already have a docsync.config.json with BYOS storage config
|
|
134
|
+
let existingConfig = null;
|
|
135
|
+
let isBYOSMode = false;
|
|
136
|
+
|
|
137
|
+
if (config.configExists()) {
|
|
138
|
+
try {
|
|
139
|
+
existingConfig = config.readConfig();
|
|
140
|
+
if (
|
|
141
|
+
existingConfig.storage &&
|
|
142
|
+
existingConfig.storage.type &&
|
|
143
|
+
existingConfig.storage.type !== "reshot"
|
|
144
|
+
) {
|
|
145
|
+
isBYOSMode = true;
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
// Config exists but couldn't be read - we'll handle this later
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Try to read CLI settings (from auth)
|
|
153
|
+
let settings = null;
|
|
154
|
+
try {
|
|
155
|
+
settings = config.readSettings();
|
|
156
|
+
} catch (readError) {
|
|
157
|
+
// No settings file - that's okay if we're in BYOS mode
|
|
158
|
+
settings = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const hasPlatformAuth = settings?.projectId && settings?.apiKey;
|
|
162
|
+
|
|
163
|
+
// If no auth and no existing BYOS config, prompt user for their choice
|
|
164
|
+
if (!hasPlatformAuth && !isBYOSMode) {
|
|
165
|
+
console.log(
|
|
166
|
+
chalk.yellow("ā No Reshot account linked. Choose how to proceed:\n")
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const { mode } = await inquirer.prompt([
|
|
170
|
+
{
|
|
171
|
+
type: "list",
|
|
172
|
+
name: "mode",
|
|
173
|
+
message: "How would you like to use Reshot?",
|
|
174
|
+
choices: [
|
|
175
|
+
{
|
|
176
|
+
name: `${chalk.cyan(
|
|
177
|
+
"Platform Mode"
|
|
178
|
+
)} - Full features: CDN, approval workflows, changelogs (requires auth)`,
|
|
179
|
+
value: "platform",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: `${chalk.green(
|
|
183
|
+
"Standalone Mode"
|
|
184
|
+
)} - Local-only: capture, diff, and output templating - no account needed`,
|
|
185
|
+
value: "standalone",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: `${chalk.blue(
|
|
189
|
+
"BYOS Mode"
|
|
190
|
+
)} - Bring Your Own Storage (S3, R2, or local) - works standalone`,
|
|
191
|
+
value: "byos",
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
if (mode === "platform") {
|
|
198
|
+
console.log(chalk.yellow("\nā Platform mode requires authentication."));
|
|
199
|
+
console.log(
|
|
200
|
+
`Run ${chalk.bold("reshot auth")} first, then run ${chalk.bold(
|
|
201
|
+
"reshot init"
|
|
202
|
+
)} again.\n`
|
|
203
|
+
);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (mode === "standalone") {
|
|
208
|
+
// Initialize standalone mode with minimal prompts
|
|
209
|
+
const { projectName } = await inquirer.prompt([
|
|
210
|
+
{
|
|
211
|
+
type: "input",
|
|
212
|
+
name: "projectName",
|
|
213
|
+
message: "Project name:",
|
|
214
|
+
default: require("path").basename(process.cwd()),
|
|
215
|
+
},
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
const { baseUrl } = await inquirer.prompt([
|
|
219
|
+
{
|
|
220
|
+
type: "input",
|
|
221
|
+
name: "baseUrl",
|
|
222
|
+
message: "Application URL to capture:",
|
|
223
|
+
default: "http://localhost:3000",
|
|
224
|
+
},
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
// Use standalone mode initialization
|
|
228
|
+
const standaloneConfig = initStandaloneMode({ projectName, force: true });
|
|
229
|
+
|
|
230
|
+
// Update baseUrl if different from default
|
|
231
|
+
if (baseUrl !== standaloneConfig.baseUrl) {
|
|
232
|
+
standaloneConfig.baseUrl = baseUrl;
|
|
233
|
+
config.writeConfig(standaloneConfig);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
printModeStatus();
|
|
237
|
+
|
|
238
|
+
console.log(chalk.cyan("\nNext steps:"));
|
|
239
|
+
console.log(
|
|
240
|
+
` 1. Run ${chalk.bold("reshot ui")} to launch the Reshot Studio`
|
|
241
|
+
);
|
|
242
|
+
console.log(` 2. Record your first scenario using the recorder`);
|
|
243
|
+
console.log(
|
|
244
|
+
` 3. Run ${chalk.bold("reshot run")} to capture screenshots\n`
|
|
245
|
+
);
|
|
246
|
+
console.log(
|
|
247
|
+
chalk.gray(
|
|
248
|
+
"Tip: Configure output templates to control where files are saved."
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
console.log(
|
|
252
|
+
chalk.gray(
|
|
253
|
+
' Example: output.template = "./docs/{{locale}}/{{name}}.png"\n'
|
|
254
|
+
)
|
|
255
|
+
);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// BYOS mode - prompt for storage configuration
|
|
260
|
+
const { storageType } = await inquirer.prompt([
|
|
261
|
+
{
|
|
262
|
+
type: "list",
|
|
263
|
+
name: "storageType",
|
|
264
|
+
message: "Select your storage provider:",
|
|
265
|
+
choices: [
|
|
266
|
+
{ name: "AWS S3", value: "s3" },
|
|
267
|
+
{ name: "Cloudflare R2", value: "r2" },
|
|
268
|
+
{ name: "Local filesystem", value: "local" },
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
// Create a basic BYOS config with sane defaults
|
|
274
|
+
const byosConfig = {
|
|
275
|
+
$schema: "https://reshot.dev/schemas/docsync-config.json",
|
|
276
|
+
version: "2.0",
|
|
277
|
+
baseUrl: "http://localhost:3000",
|
|
278
|
+
assetDir: ".reshot/output",
|
|
279
|
+
viewport: { width: 1280, height: 720 },
|
|
280
|
+
timeout: 30000,
|
|
281
|
+
headless: true,
|
|
282
|
+
storage: {
|
|
283
|
+
type: storageType,
|
|
284
|
+
},
|
|
285
|
+
scenarios: [],
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
if (storageType === "s3") {
|
|
289
|
+
const answers = await inquirer.prompt([
|
|
290
|
+
{
|
|
291
|
+
type: "input",
|
|
292
|
+
name: "bucket",
|
|
293
|
+
message: "S3 bucket name:",
|
|
294
|
+
validate: (input) => input.length > 0 || "Bucket name is required",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
type: "input",
|
|
298
|
+
name: "region",
|
|
299
|
+
message: "AWS region (e.g., us-east-1):",
|
|
300
|
+
default: "us-east-1",
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
type: "input",
|
|
304
|
+
name: "pathPrefix",
|
|
305
|
+
message: "Path prefix for assets (optional):",
|
|
306
|
+
default: "reshot-assets/",
|
|
307
|
+
},
|
|
308
|
+
]);
|
|
309
|
+
byosConfig.storage = { type: "s3", ...answers };
|
|
310
|
+
} else if (storageType === "r2") {
|
|
311
|
+
const answers = await inquirer.prompt([
|
|
312
|
+
{
|
|
313
|
+
type: "input",
|
|
314
|
+
name: "bucket",
|
|
315
|
+
message: "R2 bucket name:",
|
|
316
|
+
validate: (input) => input.length > 0 || "Bucket name is required",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
type: "input",
|
|
320
|
+
name: "accountId",
|
|
321
|
+
message: "Cloudflare Account ID:",
|
|
322
|
+
validate: (input) => input.length > 0 || "Account ID is required",
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
type: "input",
|
|
326
|
+
name: "pathPrefix",
|
|
327
|
+
message: "Path prefix for assets (optional):",
|
|
328
|
+
default: "reshot-assets/",
|
|
329
|
+
},
|
|
330
|
+
]);
|
|
331
|
+
byosConfig.storage = { type: "r2", ...answers };
|
|
332
|
+
} else if (storageType === "local") {
|
|
333
|
+
const answers = await inquirer.prompt([
|
|
334
|
+
{
|
|
335
|
+
type: "input",
|
|
336
|
+
name: "outputDir",
|
|
337
|
+
message: "Local output directory:",
|
|
338
|
+
default: "./.reshot/published",
|
|
339
|
+
},
|
|
340
|
+
]);
|
|
341
|
+
byosConfig.storage = { type: "local", ...answers };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Validate the storage configuration
|
|
345
|
+
const validation = validateStorageConfig(byosConfig.storage);
|
|
346
|
+
if (!validation.valid) {
|
|
347
|
+
console.log(chalk.red("\nā Storage configuration errors:"));
|
|
348
|
+
validation.errors.forEach((e) => console.log(chalk.red(` - ${e}`)));
|
|
349
|
+
console.log(getStorageSetupHelp(storageType));
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
if (validation.warnings.length > 0) {
|
|
353
|
+
console.log(chalk.yellow("\nā Warnings:"));
|
|
354
|
+
validation.warnings.forEach((w) => console.log(chalk.yellow(` - ${w}`)));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Save the config
|
|
358
|
+
config.writeConfig(byosConfig);
|
|
359
|
+
|
|
360
|
+
console.log(
|
|
361
|
+
chalk.green(
|
|
362
|
+
"\nā Created docsync.config.json with BYOS storage configuration"
|
|
363
|
+
)
|
|
364
|
+
);
|
|
365
|
+
console.log(chalk.cyan("\nNext steps:"));
|
|
366
|
+
console.log(
|
|
367
|
+
` 1. Set required environment variables for ${storageType.toUpperCase()} storage`
|
|
368
|
+
);
|
|
369
|
+
if (storageType === "s3") {
|
|
370
|
+
console.log(chalk.gray(' export AWS_ACCESS_KEY_ID="..."'));
|
|
371
|
+
console.log(chalk.gray(' export AWS_SECRET_ACCESS_KEY="..."'));
|
|
372
|
+
} else if (storageType === "r2") {
|
|
373
|
+
console.log(chalk.gray(' export R2_ACCESS_KEY_ID="..."'));
|
|
374
|
+
console.log(chalk.gray(' export R2_SECRET_ACCESS_KEY="..."'));
|
|
375
|
+
}
|
|
376
|
+
console.log(
|
|
377
|
+
` 2. Run ${chalk.bold(
|
|
378
|
+
'reshot record "My Visual"'
|
|
379
|
+
)} to capture your first flow`
|
|
380
|
+
);
|
|
381
|
+
console.log(
|
|
382
|
+
` 3. Run ${chalk.bold("reshot publish")} to upload to your storage\n`
|
|
383
|
+
);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Platform mode with auth OR updating existing config
|
|
388
|
+
if (hasPlatformAuth) {
|
|
389
|
+
const { projectId, apiKey } = settings;
|
|
390
|
+
|
|
391
|
+
let overwrite = false;
|
|
392
|
+
if (config.configExists()) {
|
|
393
|
+
const answer = await inquirer.prompt([
|
|
394
|
+
{
|
|
395
|
+
type: "confirm",
|
|
396
|
+
name: "overwrite",
|
|
397
|
+
default: false,
|
|
398
|
+
message:
|
|
399
|
+
"docsync.config.json already exists. Overwrite it with the latest blueprint?",
|
|
400
|
+
},
|
|
401
|
+
]);
|
|
402
|
+
overwrite = answer.overwrite;
|
|
403
|
+
|
|
404
|
+
if (!overwrite) {
|
|
405
|
+
console.log(chalk.yellow("ā Existing docsync.config.json preserved."));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
const blueprint = await config.initializeProject(projectId, apiKey, {
|
|
412
|
+
overwrite,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Auto-detect and configure documentation
|
|
416
|
+
console.log(chalk.cyan("\nš Configuring documentation sync..."));
|
|
417
|
+
|
|
418
|
+
let docsRoot = detectDocumentationRoot();
|
|
419
|
+
let shouldScaffold = false;
|
|
420
|
+
|
|
421
|
+
if (!docsRoot) {
|
|
422
|
+
console.log(chalk.yellow(" No documentation directory detected."));
|
|
423
|
+
|
|
424
|
+
const { action } = await inquirer.prompt([
|
|
425
|
+
{
|
|
426
|
+
type: "list",
|
|
427
|
+
name: "action",
|
|
428
|
+
message: "Would you like to:",
|
|
429
|
+
choices: [
|
|
430
|
+
{
|
|
431
|
+
name: "Create a new docs/ directory with examples",
|
|
432
|
+
value: "scaffold",
|
|
433
|
+
},
|
|
434
|
+
{ name: "Specify an existing directory", value: "specify" },
|
|
435
|
+
{ name: "Skip documentation setup (add later)", value: "skip" },
|
|
436
|
+
],
|
|
437
|
+
},
|
|
438
|
+
]);
|
|
439
|
+
|
|
440
|
+
if (action === "scaffold") {
|
|
441
|
+
console.log(chalk.cyan(" Creating docs/ directory..."));
|
|
442
|
+
docsRoot = await scaffoldDocumentation();
|
|
443
|
+
shouldScaffold = true;
|
|
444
|
+
console.log(chalk.green(" ā Created docs/ with example files"));
|
|
445
|
+
} else if (action === "specify") {
|
|
446
|
+
const { customPath } = await inquirer.prompt([
|
|
447
|
+
{
|
|
448
|
+
type: "input",
|
|
449
|
+
name: "customPath",
|
|
450
|
+
message: "Documentation directory path:",
|
|
451
|
+
default: "./docs",
|
|
452
|
+
validate: (input) => {
|
|
453
|
+
const fullPath = path.join(process.cwd(), input);
|
|
454
|
+
if (!fs.existsSync(fullPath)) {
|
|
455
|
+
return `Directory ${input} does not exist. Create it first or choose scaffold option.`;
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
]);
|
|
461
|
+
docsRoot = customPath;
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
console.log(chalk.green(` ā Detected documentation at: ${docsRoot}`));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Add documentation block to config
|
|
468
|
+
if (docsRoot) {
|
|
469
|
+
// Detect if this is a GitHub repo for strategy recommendation
|
|
470
|
+
const isGitRepo = fs.existsSync(path.join(process.cwd(), ".git"));
|
|
471
|
+
const hasGitHubRemote =
|
|
472
|
+
isGitRepo &&
|
|
473
|
+
(() => {
|
|
474
|
+
try {
|
|
475
|
+
const { execSync } = require("child_process");
|
|
476
|
+
const remote = execSync("git remote get-url origin", {
|
|
477
|
+
encoding: "utf-8",
|
|
478
|
+
});
|
|
479
|
+
return remote.includes("github.com");
|
|
480
|
+
} catch {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
})();
|
|
484
|
+
|
|
485
|
+
let strategy = "git_pr";
|
|
486
|
+
|
|
487
|
+
if (hasGitHubRemote) {
|
|
488
|
+
console.log(chalk.cyan(" ā Detected GitHub repository"));
|
|
489
|
+
const { useGitPR } = await inquirer.prompt([
|
|
490
|
+
{
|
|
491
|
+
type: "confirm",
|
|
492
|
+
name: "useGitPR",
|
|
493
|
+
message:
|
|
494
|
+
"Automatically create Pull Requests for documentation updates?",
|
|
495
|
+
default: true,
|
|
496
|
+
},
|
|
497
|
+
]);
|
|
498
|
+
strategy = useGitPR ? "git_pr" : "external_host";
|
|
499
|
+
} else {
|
|
500
|
+
const { strategyChoice } = await inquirer.prompt([
|
|
501
|
+
{
|
|
502
|
+
type: "list",
|
|
503
|
+
name: "strategyChoice",
|
|
504
|
+
message: "How should documentation updates be delivered?",
|
|
505
|
+
choices: [
|
|
506
|
+
{
|
|
507
|
+
name: "Git Pull Requests (recommended for GitHub/GitLab repos)",
|
|
508
|
+
value: "git_pr",
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "Notifications only (for external CMS like ReadMe, GitBook)",
|
|
512
|
+
value: "external_host",
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
},
|
|
516
|
+
]);
|
|
517
|
+
strategy = strategyChoice;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
blueprint.documentation = generateDocumentationConfig(
|
|
521
|
+
docsRoot,
|
|
522
|
+
strategy
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
// Save updated config
|
|
526
|
+
config.writeConfig(blueprint);
|
|
527
|
+
|
|
528
|
+
console.log(chalk.green(" ā Documentation sync configured"));
|
|
529
|
+
console.log(chalk.gray(` Root: ${docsRoot}`));
|
|
530
|
+
console.log(chalk.gray(` Strategy: ${strategy}`));
|
|
531
|
+
|
|
532
|
+
if (shouldScaffold) {
|
|
533
|
+
console.log(chalk.cyan("\n š Example files created:"));
|
|
534
|
+
console.log(chalk.gray(" - docs/README.md"));
|
|
535
|
+
console.log(chalk.gray(" - docs/getting-started.md"));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (blueprint._metadata?.projectName) {
|
|
540
|
+
console.log(chalk.green("ā Pulled docsync.config.json from Reshot"));
|
|
541
|
+
} else {
|
|
542
|
+
console.log(
|
|
543
|
+
chalk.yellow(
|
|
544
|
+
"ā Unable to fetch existing blueprint from Reshot. Using boilerplate template instead."
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
console.log(chalk.green("ā Saved docsync.config.json"));
|
|
549
|
+
|
|
550
|
+
const updatedSettings = config.readSettings();
|
|
551
|
+
console.log("");
|
|
552
|
+
console.log(
|
|
553
|
+
chalk.cyan(
|
|
554
|
+
`⨠Reshot initialized for ${
|
|
555
|
+
updatedSettings.projectName || "your project"
|
|
556
|
+
} (${projectId})`
|
|
557
|
+
)
|
|
558
|
+
);
|
|
559
|
+
console.log("\nNext steps:");
|
|
560
|
+
console.log(
|
|
561
|
+
` 1. Review ${chalk.bold(
|
|
562
|
+
"docsync.config.json"
|
|
563
|
+
)} and commit it to your repo.`
|
|
564
|
+
);
|
|
565
|
+
console.log(
|
|
566
|
+
` ${chalk.gray(
|
|
567
|
+
"Note: Only the JSON file is committed ā binaries stream via CLI + API."
|
|
568
|
+
)}`
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
if (blueprint.documentation) {
|
|
572
|
+
console.log(
|
|
573
|
+
` 2. Add ${chalk.bold("reshot_journey")} frontmatter to your docs:`
|
|
574
|
+
);
|
|
575
|
+
console.log(
|
|
576
|
+
chalk.gray(
|
|
577
|
+
' ---\n title: "Feature Name"\n reshot_journey: "feature/workflow"\n ---'
|
|
578
|
+
)
|
|
579
|
+
);
|
|
580
|
+
console.log(
|
|
581
|
+
` 3. Run ${chalk.bold(
|
|
582
|
+
"reshot validate"
|
|
583
|
+
)} to check your documentation setup.`
|
|
584
|
+
);
|
|
585
|
+
console.log(
|
|
586
|
+
` 4. Run Playwright tests to generate traces, then ${chalk.bold(
|
|
587
|
+
"reshot ingest"
|
|
588
|
+
)}`
|
|
589
|
+
);
|
|
590
|
+
console.log(
|
|
591
|
+
` 5. Push your branch - Reshot will detect drift and create PRs.`
|
|
592
|
+
);
|
|
593
|
+
} else {
|
|
594
|
+
console.log(
|
|
595
|
+
` 2. Run ${chalk.bold(
|
|
596
|
+
'reshot record "My Visual"'
|
|
597
|
+
)} to capture your first flow.`
|
|
598
|
+
);
|
|
599
|
+
console.log(
|
|
600
|
+
` 3. Push your branch and open a PR. Reshot will post visual changes as a PR comment.`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
console.log("");
|
|
604
|
+
} catch (error) {
|
|
605
|
+
console.error(chalk.red("Failed to initialize:"), error.message);
|
|
606
|
+
process.exit(1);
|
|
607
|
+
}
|
|
608
|
+
} else if (isBYOSMode) {
|
|
609
|
+
// Existing BYOS config - just validate and show info
|
|
610
|
+
console.log(chalk.green("ā Found existing BYOS configuration"));
|
|
611
|
+
const validation = validateStorageConfig(existingConfig.storage);
|
|
612
|
+
if (!validation.valid) {
|
|
613
|
+
console.log(chalk.red("\nā Storage configuration errors:"));
|
|
614
|
+
validation.errors.forEach((e) => console.log(chalk.red(` - ${e}`)));
|
|
615
|
+
console.log(getStorageSetupHelp(existingConfig.storage.type));
|
|
616
|
+
} else {
|
|
617
|
+
console.log(chalk.cyan(` Storage type: ${existingConfig.storage.type}`));
|
|
618
|
+
if (existingConfig.storage.bucket) {
|
|
619
|
+
console.log(chalk.cyan(` Bucket: ${existingConfig.storage.bucket}`));
|
|
620
|
+
}
|
|
621
|
+
console.log(chalk.green("\nā Configuration is valid"));
|
|
622
|
+
console.log(chalk.cyan("\nNext steps:"));
|
|
623
|
+
console.log(
|
|
624
|
+
` 1. Run ${chalk.bold('reshot record "My Visual"')} to capture a flow`
|
|
625
|
+
);
|
|
626
|
+
console.log(
|
|
627
|
+
` 2. Run ${chalk.bold("reshot publish")} to upload to your storage\n`
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
module.exports = initCommand;
|