@archastro/astroshot 0.2.1 → 0.2.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/README.md +4 -0
- package/bin/astroshot.mjs +56 -1
- package/bin/demo.mjs +72 -16
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -13,6 +13,10 @@ npx astroshot demo
|
|
|
13
13
|
# Check Node, watched folders, app install/run state, Chromium, and macOS
|
|
14
14
|
# Screen Recording. Each failure prints the exact fix command. Read-only.
|
|
15
15
|
npx astroshot doctor
|
|
16
|
+
|
|
17
|
+
# Review the stream in your terminal (Ghostty, kitty, or WezTerm draw the
|
|
18
|
+
# pictures; ffmpeg plays movies). Same review.json as the macOS app.
|
|
19
|
+
npx astroshot review
|
|
16
20
|
```
|
|
17
21
|
|
|
18
22
|
`demo` accepts `--feature <name>`, `--root <dir>`, and `--json`. `doctor`
|
package/bin/astroshot.mjs
CHANGED
|
@@ -8,12 +8,14 @@ import path from "node:path";
|
|
|
8
8
|
import { writeFixtureTemplate } from "./templates.mjs";
|
|
9
9
|
import { demoHelp, runDemo } from "./demo.mjs";
|
|
10
10
|
import { doctorHelp, runDoctor } from "./doctor.mjs";
|
|
11
|
+
import { readWatchConfiguration } from "./mac-preferences.mjs";
|
|
11
12
|
|
|
12
13
|
function help() {
|
|
13
14
|
console.log(`astroshot — one CLI for React, Ink, PTY stills, and movies
|
|
14
15
|
|
|
15
16
|
Usage:
|
|
16
|
-
astroshot
|
|
17
|
+
astroshot review [<dir>...] [--root <dir>] [--no-graphics]
|
|
18
|
+
astroshot demo [--feature <name>] [--root <dir>] [--dry-run] [--clean]
|
|
17
19
|
astroshot doctor [--root <dir>] [--json]
|
|
18
20
|
astroshot init react [fixture.tsx] [--force]
|
|
19
21
|
astroshot init ink [fixture.tsx] [--force]
|
|
@@ -28,6 +30,7 @@ Usage:
|
|
|
28
30
|
astroshot install-browser [--with-deps]
|
|
29
31
|
|
|
30
32
|
Start here:
|
|
33
|
+
review Review the .astroshot/ stream in your terminal (Kitty graphics)
|
|
31
34
|
demo Write a complete .astroshot/ example set (no prerequisites)
|
|
32
35
|
doctor Check Node, watched folders, app, Chromium, permissions
|
|
33
36
|
|
|
@@ -104,6 +107,11 @@ Options:
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
function engineBin(mode) {
|
|
110
|
+
if (mode === "review") {
|
|
111
|
+
const entry = fileURLToPath(import.meta.resolve("@archastro/astroshot-review"));
|
|
112
|
+
const packageRoot = path.dirname(path.dirname(entry));
|
|
113
|
+
return path.join(packageRoot, "bin", "astroshot-review.mjs");
|
|
114
|
+
}
|
|
107
115
|
if (mode === "movie") {
|
|
108
116
|
const entry = fileURLToPath(import.meta.resolve("@archastro/movie-harness"));
|
|
109
117
|
// package exports "." → dist/index.js → package root is two levels up from dist
|
|
@@ -189,6 +197,49 @@ function runInit(arguments_) {
|
|
|
189
197
|
console.log(`Created ${result.label} fixture: ${result.absolutePath}`);
|
|
190
198
|
}
|
|
191
199
|
|
|
200
|
+
/**
|
|
201
|
+
* `astroshot review`: the terminal tray. Without explicit roots it watches
|
|
202
|
+
* the same folders as the Astroshots app so both surfaces show one stream.
|
|
203
|
+
*/
|
|
204
|
+
function runReview(arguments_) {
|
|
205
|
+
// `astroshot review help` mirrors the other modes; anything else is
|
|
206
|
+
// forwarded verbatim so a folder literally named "help" still works.
|
|
207
|
+
const wantsHelp =
|
|
208
|
+
arguments_[0] === "help" ||
|
|
209
|
+
arguments_.includes("-h") ||
|
|
210
|
+
arguments_.includes("--help");
|
|
211
|
+
const forwarded = wantsHelp ? ["--help"] : [...arguments_];
|
|
212
|
+
const hasRoots =
|
|
213
|
+
forwarded.includes("--root") ||
|
|
214
|
+
forwarded.some((value) => !value.startsWith("-"));
|
|
215
|
+
if (!hasRoots && !wantsHelp) {
|
|
216
|
+
const configuration = readWatchConfiguration();
|
|
217
|
+
if (configuration.available && configuration.roots.length > 0) {
|
|
218
|
+
for (const root of configuration.roots) forwarded.push("--root", root);
|
|
219
|
+
forwarded.push("--roots-source", "app");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
let engine;
|
|
223
|
+
try {
|
|
224
|
+
engine = engineBin("review");
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error(
|
|
227
|
+
`astroshot could not find the review tray engine (@archastro/astroshot-review): ${error instanceof Error ? error.message : error}`,
|
|
228
|
+
);
|
|
229
|
+
console.error("Reinstall @archastro/astroshot (or the unscoped astroshot package) and retry.");
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
const result = spawnSync(process.execPath, [engine, ...forwarded], {
|
|
233
|
+
stdio: "inherit",
|
|
234
|
+
});
|
|
235
|
+
if (result.error) {
|
|
236
|
+
console.error(`astroshot could not start the review tray: ${result.error.message}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
if (result.signal) process.kill(process.pid, result.signal);
|
|
240
|
+
process.exit(result.status ?? 1);
|
|
241
|
+
}
|
|
242
|
+
|
|
192
243
|
const [command, ...arguments_] = process.argv.slice(2);
|
|
193
244
|
|
|
194
245
|
if (command === "-v" || command === "--version") {
|
|
@@ -254,6 +305,10 @@ if (command === "react" || command === "ink" || command === "tui" || command ===
|
|
|
254
305
|
runEngine(canonicalMode, arguments_);
|
|
255
306
|
}
|
|
256
307
|
|
|
308
|
+
if (command === "review" || command === "tray") {
|
|
309
|
+
runReview(arguments_);
|
|
310
|
+
}
|
|
311
|
+
|
|
257
312
|
if (command === "movie") {
|
|
258
313
|
// Always forward to movie-harness (including --help / which-source).
|
|
259
314
|
const result = spawnSync(
|
package/bin/demo.mjs
CHANGED
|
@@ -30,6 +30,8 @@ Options:
|
|
|
30
30
|
--feature <name> Feature directory under .astroshot/ (default: ${DEFAULT_DEMO_FEATURE})
|
|
31
31
|
--root <dir> Worktree root (default: git root, else cwd)
|
|
32
32
|
--json Print the written paths as JSON
|
|
33
|
+
--dry-run Print planned paths without writing
|
|
34
|
+
--clean Accepted; does not delete yet
|
|
33
35
|
-h, --help Show this help
|
|
34
36
|
|
|
35
37
|
Writes two stills, one movie poster + video pair, and manifest.json using
|
|
@@ -112,14 +114,24 @@ export function loadDemoFixtures(fixturesDirectory = FIXTURES_DIR) {
|
|
|
112
114
|
return index;
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
function publicDemoResult(plan) {
|
|
118
|
+
return {
|
|
119
|
+
root: plan.root,
|
|
120
|
+
feature: plan.feature,
|
|
121
|
+
featureDirectory: plan.featureDirectory,
|
|
122
|
+
manifestPath: plan.manifestPath,
|
|
123
|
+
runId: plan.runId,
|
|
124
|
+
shots: plan.shots,
|
|
125
|
+
files: plan.files,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
115
129
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* earlier numbered frames stay on disk as prior-run evidence. That matches the
|
|
120
|
-
* documented lifecycle used by astroshot-capture and the movie harness.
|
|
130
|
+
* Read-only plan of the next demo write: same root/feature resolve, kebab-case
|
|
131
|
+
* check, fixtures, next sequence, and file names (including manifest.json).
|
|
132
|
+
* Does not mkdir or write.
|
|
121
133
|
*/
|
|
122
|
-
export function
|
|
134
|
+
export function planDemo({
|
|
123
135
|
root,
|
|
124
136
|
feature = DEFAULT_DEMO_FEATURE,
|
|
125
137
|
fixturesDirectory = FIXTURES_DIR,
|
|
@@ -130,7 +142,6 @@ export function writeDemo({
|
|
|
130
142
|
const featureDirectory = path.join(resolvedRoot, ".astroshot", feature);
|
|
131
143
|
const index = loadDemoFixtures(fixturesDirectory);
|
|
132
144
|
|
|
133
|
-
fs.mkdirSync(featureDirectory, { recursive: true });
|
|
134
145
|
const startSequence = nextSequence(featureDirectory);
|
|
135
146
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
136
147
|
// The first sequence of this run keeps the id unique when two runs land in
|
|
@@ -140,11 +151,16 @@ export function writeDemo({
|
|
|
140
151
|
|
|
141
152
|
const shots = [];
|
|
142
153
|
const files = [];
|
|
154
|
+
const copies = [];
|
|
143
155
|
index.shots.forEach((fixture, offset) => {
|
|
144
156
|
const sequence = String(startSequence + offset).padStart(4, "0");
|
|
145
157
|
const posterName = `${sequence}-${fixture.slug}.png`;
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
const posterPath = path.join(featureDirectory, posterName);
|
|
159
|
+
copies.push({
|
|
160
|
+
source: path.join(fixturesDirectory, fixture.asset),
|
|
161
|
+
destination: posterPath,
|
|
162
|
+
});
|
|
163
|
+
files.push(posterPath);
|
|
148
164
|
|
|
149
165
|
const shot = {
|
|
150
166
|
id: sequence,
|
|
@@ -159,8 +175,12 @@ export function writeDemo({
|
|
|
159
175
|
if (fixture.video) {
|
|
160
176
|
const videoExtension = path.extname(fixture.video) || ".webm";
|
|
161
177
|
const videoName = `${sequence}-${fixture.slug}${videoExtension}`;
|
|
162
|
-
|
|
163
|
-
|
|
178
|
+
const videoPath = path.join(featureDirectory, videoName);
|
|
179
|
+
copies.push({
|
|
180
|
+
source: path.join(fixturesDirectory, fixture.video),
|
|
181
|
+
destination: videoPath,
|
|
182
|
+
});
|
|
183
|
+
files.push(videoPath);
|
|
164
184
|
shot.kind = "movie";
|
|
165
185
|
shot.video = videoName;
|
|
166
186
|
shot.duration_ms = fixture.duration_ms;
|
|
@@ -181,7 +201,6 @@ export function writeDemo({
|
|
|
181
201
|
"Synthetic proof set written by `astroshot demo` — stills plus one journey movie.",
|
|
182
202
|
shots,
|
|
183
203
|
};
|
|
184
|
-
writeAtomic(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
185
204
|
files.push(manifestPath);
|
|
186
205
|
|
|
187
206
|
return {
|
|
@@ -189,12 +208,31 @@ export function writeDemo({
|
|
|
189
208
|
feature,
|
|
190
209
|
featureDirectory,
|
|
191
210
|
manifestPath,
|
|
211
|
+
manifest,
|
|
192
212
|
runId,
|
|
193
213
|
shots,
|
|
194
214
|
files,
|
|
215
|
+
copies,
|
|
195
216
|
};
|
|
196
217
|
}
|
|
197
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Write the demo set and return the paths, without printing anything.
|
|
221
|
+
*
|
|
222
|
+
* Each invocation is its own run: a new `run_id` with a fresh shot list, while
|
|
223
|
+
* earlier numbered frames stay on disk as prior-run evidence. That matches the
|
|
224
|
+
* documented lifecycle used by astroshot-capture and the movie harness.
|
|
225
|
+
*/
|
|
226
|
+
export function writeDemo(options = {}) {
|
|
227
|
+
const plan = planDemo(options);
|
|
228
|
+
fs.mkdirSync(plan.featureDirectory, { recursive: true });
|
|
229
|
+
for (const { source, destination } of plan.copies) {
|
|
230
|
+
copyAtomic(source, destination);
|
|
231
|
+
}
|
|
232
|
+
writeAtomic(plan.manifestPath, `${JSON.stringify(plan.manifest, null, 2)}\n`);
|
|
233
|
+
return publicDemoResult(plan);
|
|
234
|
+
}
|
|
235
|
+
|
|
198
236
|
function coverageAdvice(root) {
|
|
199
237
|
const configuration = readWatchConfiguration();
|
|
200
238
|
const coverage = evaluateWatchCoverage(root, configuration);
|
|
@@ -231,7 +269,13 @@ function coverageAdvice(root) {
|
|
|
231
269
|
}
|
|
232
270
|
|
|
233
271
|
export function runDemo(argv, { log = console.log } = {}) {
|
|
234
|
-
const options = {
|
|
272
|
+
const options = {
|
|
273
|
+
feature: DEFAULT_DEMO_FEATURE,
|
|
274
|
+
root: undefined,
|
|
275
|
+
json: false,
|
|
276
|
+
dryRun: false,
|
|
277
|
+
clean: false,
|
|
278
|
+
};
|
|
235
279
|
for (let index = 0; index < argv.length; index += 1) {
|
|
236
280
|
const token = argv[index];
|
|
237
281
|
if (token === "-h" || token === "--help" || token === "help") {
|
|
@@ -242,6 +286,14 @@ export function runDemo(argv, { log = console.log } = {}) {
|
|
|
242
286
|
options.json = true;
|
|
243
287
|
continue;
|
|
244
288
|
}
|
|
289
|
+
if (token === "--dry-run") {
|
|
290
|
+
options.dryRun = true;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (token === "--clean") {
|
|
294
|
+
options.clean = true;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
245
297
|
if (token === "--feature" || token === "--root") {
|
|
246
298
|
const value = argv[index + 1];
|
|
247
299
|
if (!value || value.startsWith("-")) {
|
|
@@ -254,18 +306,22 @@ export function runDemo(argv, { log = console.log } = {}) {
|
|
|
254
306
|
throw new Error(`Unknown demo argument: ${token}`);
|
|
255
307
|
}
|
|
256
308
|
|
|
257
|
-
const result =
|
|
309
|
+
const result = options.dryRun
|
|
310
|
+
? publicDemoResult(planDemo(options))
|
|
311
|
+
: writeDemo(options);
|
|
258
312
|
if (options.json) {
|
|
259
313
|
log(JSON.stringify({ ...result, advice: coverageAdvice(result.root) }, null, 2));
|
|
260
314
|
return 0;
|
|
261
315
|
}
|
|
262
316
|
|
|
263
|
-
const movies = result.shots.filter((shot) => shot.kind === "movie").length;
|
|
264
|
-
const stills = result.shots.length - movies;
|
|
265
317
|
log(`astroshot demo → ${result.featureDirectory}`);
|
|
266
318
|
for (const file of result.files) {
|
|
267
319
|
log(` ${path.relative(result.root, file)}`);
|
|
268
320
|
}
|
|
321
|
+
if (options.dryRun) return 0;
|
|
322
|
+
|
|
323
|
+
const movies = result.shots.filter((shot) => shot.kind === "movie").length;
|
|
324
|
+
const stills = result.shots.length - movies;
|
|
269
325
|
log("");
|
|
270
326
|
log(
|
|
271
327
|
`Wrote ${stills} still${stills === 1 ? "" : "s"}, ${movies} movie${movies === 1 ? "" : "s"} (poster + video), and manifest.json.`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archastro/astroshot",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "One CLI for deterministic React, terminal, and movie UI captures",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -72,8 +72,9 @@
|
|
|
72
72
|
"registry": "https://registry.npmjs.org/"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@archastro/
|
|
76
|
-
"@archastro/
|
|
77
|
-
"@archastro/
|
|
75
|
+
"@archastro/astroshot-review": "0.2.2",
|
|
76
|
+
"@archastro/movie-harness": "0.2.2",
|
|
77
|
+
"@archastro/react-shot": "0.2.2",
|
|
78
|
+
"@archastro/tui-shot": "0.2.2"
|
|
78
79
|
}
|
|
79
80
|
}
|