@pixelvibing/cli 0.54.0 → 0.55.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/dist/pixel-vibing-cli.js
CHANGED
|
@@ -3428,8 +3428,9 @@ function setPieces(asset) {
|
|
|
3428
3428
|
function namesOf(name, count) {
|
|
3429
3429
|
return count === 1 ? [name] : Array.from({ length: count }, (_, index) => `${name}/${index}`);
|
|
3430
3430
|
}
|
|
3431
|
-
function nameFor(asset, record, given2) {
|
|
3432
|
-
const
|
|
3431
|
+
function nameFor(asset, record, given2, key, hd) {
|
|
3432
|
+
const platform = asset.name ?? SET_NAMES[asset.kind] ?? `${asset.kind}-${asset.id.slice(0, 8)}`;
|
|
3433
|
+
const name = given2 ?? record.assets[key]?.name ?? (hd ? `${platform}-hd` : platform);
|
|
3433
3434
|
const problem = nameProblem(name);
|
|
3434
3435
|
if (problem !== null) throw new CliError(`${name} is not a name a picture can be landed under (${problem}): give one with --as`);
|
|
3435
3436
|
return name;
|
|
@@ -3442,13 +3443,22 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3442
3443
|
if (asset.kind === "terrain") {
|
|
3443
3444
|
throw new CliError(`${asset.id} is a terrain set: land it from the editor's Library, which cuts a set into the cells its brush paints. Nothing was landed`);
|
|
3444
3445
|
}
|
|
3445
|
-
const record = await readRecord(local);
|
|
3446
|
-
const name = nameFor(asset, record, given2);
|
|
3447
3446
|
const pieces = setPieces(asset);
|
|
3447
|
+
const hd = options.hd === true;
|
|
3448
|
+
const drawnSmooth = asset.data.pixelArt === false;
|
|
3449
|
+
if (hd && pieces === null) throw new CliError(`${asset.id} is a ${asset.kind}: only a UI's pieces and icons have an HD version. Nothing was landed`);
|
|
3450
|
+
if (hd && !drawnSmooth && pieces?.some((piece) => piece.hd === void 0 || asset.attachments?.[piece.hd] === void 0)) {
|
|
3451
|
+
throw new CliError(`${asset.id} was made before its pieces were kept as drawn, so it has no HD version. Nothing was landed`);
|
|
3452
|
+
}
|
|
3453
|
+
const lifted = hd && !drawnSmooth;
|
|
3454
|
+
const key = lifted ? `${asset.id}/hd` : asset.id;
|
|
3455
|
+
const record = await readRecord(local);
|
|
3456
|
+
const name = nameFor(asset, record, given2, key, lifted);
|
|
3448
3457
|
const reference = REFERENCE_KINDS.has(asset.kind);
|
|
3449
3458
|
const names = pieces === null ? namesOf(name, asset.images.length) : pieceNames(name, pieces);
|
|
3450
3459
|
const files = names.map((each) => `${reference ? "ref" : "out"}/${each}.png`);
|
|
3451
|
-
const
|
|
3460
|
+
const version2 = pieces === null ? {} : { version: drawnSmooth || lifted ? "hd" : "pixel-art" };
|
|
3461
|
+
const pulled = { id: asset.id, kind: asset.kind, name, hash: asset.hash, files, ...version2, data: asset.data };
|
|
3452
3462
|
const base = `/projects/${encodeURIComponent(project)}`;
|
|
3453
3463
|
const json2 = async (path, init2) => (await platform.call(path, init2)).json();
|
|
3454
3464
|
const art = await json2(`${base}/art`);
|
|
@@ -3456,7 +3466,9 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3456
3466
|
const held = (each) => reference ? references.get(each) : art.pictures[each];
|
|
3457
3467
|
const landing = [];
|
|
3458
3468
|
for (const [index, each] of names.entries()) {
|
|
3459
|
-
const
|
|
3469
|
+
const own = asset.images[index];
|
|
3470
|
+
const drawn = lifted ? asset.attachments?.[pieces?.[index]?.hd] : void 0;
|
|
3471
|
+
const image = drawn?.id ?? own.id;
|
|
3460
3472
|
const holds = held(each);
|
|
3461
3473
|
if (holds === image) continue;
|
|
3462
3474
|
if (holds !== void 0) {
|
|
@@ -3464,7 +3476,8 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3464
3476
|
`the project's art already holds ${reference ? "a reference" : "a picture"} named ${each}, of something else: nothing was landed. Give this one another name with --as`
|
|
3465
3477
|
);
|
|
3466
3478
|
}
|
|
3467
|
-
|
|
3479
|
+
const piece = pieces?.[index] ?? null;
|
|
3480
|
+
landing.push({ name: each, image, piece: piece === null || drawn === void 0 ? piece : asDrawn(piece, drawn.width / own.width) });
|
|
3468
3481
|
}
|
|
3469
3482
|
if (landing.length > 0) {
|
|
3470
3483
|
if (reference) {
|
|
@@ -3478,13 +3491,24 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3478
3491
|
await json2(`${base}/art/land`, { method: "POST", body: { revision: art.revision, assets } });
|
|
3479
3492
|
}
|
|
3480
3493
|
}
|
|
3481
|
-
await writeRecord(local, { ...record, assets: { ...record.assets, [
|
|
3494
|
+
await writeRecord(local, { ...record, assets: { ...record.assets, [key]: { kind: asset.kind, name, hash: asset.hash, files } } });
|
|
3482
3495
|
await bringFolder(platform, local, { map: false, aside: options.aside === true });
|
|
3483
3496
|
return { ...pulled, current: landing.length === 0 };
|
|
3484
3497
|
}
|
|
3498
|
+
function asDrawn(piece, scale) {
|
|
3499
|
+
if (piece.nineGrid === void 0 || !Number.isFinite(scale) || scale <= 1) return piece;
|
|
3500
|
+
const grown = (inset) => Math.round(inset * scale);
|
|
3501
|
+
const { top, right, bottom, left } = piece.nineGrid;
|
|
3502
|
+
return { ...piece, nineGrid: { top: grown(top), right: grown(right), bottom: grown(bottom), left: grown(left) } };
|
|
3503
|
+
}
|
|
3485
3504
|
function pulledWords(pulled) {
|
|
3486
3505
|
const where = pulled.files.length === 1 ? `pixelvibing/${pulled.files[0]}` : `${pulled.files.length} pictures under pixelvibing/`;
|
|
3487
|
-
|
|
3506
|
+
const said = pulled.current ? `${pulled.name} is current: the project's art holds it, and ${where} is what the platform has.` : `Pulled ${pulled.name}, ${pulled.kind}, into the project's art, and wrote ${where}.`;
|
|
3507
|
+
if (pulled.version === "hd" && pulled.data.pixelArt === false)
|
|
3508
|
+
return `${said} It was drawn without pixel art, so its pieces are HD and there is no pixel-art version.`;
|
|
3509
|
+
if (pulled.version === "hd") return `${said} These are the pieces as drawn (HD).`;
|
|
3510
|
+
if (pulled.version === "pixel-art") return `${said} These are the pixel-art pieces; --hd lands them as drawn.`;
|
|
3511
|
+
return said;
|
|
3488
3512
|
}
|
|
3489
3513
|
const date = (at) => at.slice(0, 10);
|
|
3490
3514
|
function listed(assets) {
|
|
@@ -3518,9 +3542,10 @@ More: pixel-vibing-cli list${kind === void 0 ? "" : ` ${kind}`}${tag2 === null ?
|
|
|
3518
3542
|
const pull = {
|
|
3519
3543
|
name: "pull",
|
|
3520
3544
|
summary: "Land one asset, by its id or its name, in the project's art, and write it into the local project",
|
|
3521
|
-
usage: "<id|name> [--as <name>] [--aside]",
|
|
3545
|
+
usage: "<id|name> [--as <name>] [--aside] [--hd]",
|
|
3522
3546
|
options: {
|
|
3523
3547
|
as: { type: "string", value: "<name>", description: "land it under this name instead of the platform's" },
|
|
3548
|
+
hd: { type: "boolean", description: "land a UI's pieces or icons as drawn (HD), beside the pixel-art ones, instead of read back on the pixel grid" },
|
|
3524
3549
|
aside: { type: "boolean", description: "keep a folder's own map and art under pixelvibing/before-platform/, and write it from the platform" }
|
|
3525
3550
|
},
|
|
3526
3551
|
positionals: [1, 1],
|
|
@@ -3528,7 +3553,10 @@ const pull = {
|
|
|
3528
3553
|
const local = await projectHere(context.io.cwd);
|
|
3529
3554
|
linkedOf(local);
|
|
3530
3555
|
const platform = await signedIn(context);
|
|
3531
|
-
const pulled = await pullAsset(platform, local, args.positionals[0], stringOf(args, "as"), {
|
|
3556
|
+
const pulled = await pullAsset(platform, local, args.positionals[0], stringOf(args, "as"), {
|
|
3557
|
+
aside: flagOf(args, "aside"),
|
|
3558
|
+
hd: flagOf(args, "hd")
|
|
3559
|
+
});
|
|
3532
3560
|
answer(context, pulled, pulledWords(pulled));
|
|
3533
3561
|
}
|
|
3534
3562
|
};
|
|
@@ -4508,33 +4536,58 @@ function shapeProblem(shape, index, canvas) {
|
|
|
4508
4536
|
return null;
|
|
4509
4537
|
}
|
|
4510
4538
|
function sketchProblem(sketch) {
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4539
|
+
const problems = sketchProblems(sketch);
|
|
4540
|
+
return problems.length === 0 ? null : problems.join("; ");
|
|
4541
|
+
}
|
|
4542
|
+
function sketchProblems(sketch) {
|
|
4543
|
+
const problems = [];
|
|
4544
|
+
if (typeof sketch.instruction !== "string" || sketch.instruction.trim() === "") problems.push("the sketch has no description");
|
|
4545
|
+
else if (sketch.instruction.length > MAX_SKETCH_INSTRUCTION)
|
|
4546
|
+
problems.push(`the description is ${sketch.instruction.length} characters; it is at most ${MAX_SKETCH_INSTRUCTION}`);
|
|
4514
4547
|
const { canvas, shapes } = sketch;
|
|
4515
4548
|
if (!SKETCH_CANVASES.some((size) => size.width === canvas.width && size.height === canvas.height)) {
|
|
4516
|
-
|
|
4549
|
+
problems.push(
|
|
4550
|
+
`the canvas is ${canvas.width} × ${canvas.height}; a sketch is ${SKETCH_CANVASES.map((size) => `${size.width} × ${size.height}`).join(", ")}`
|
|
4551
|
+
);
|
|
4552
|
+
return problems;
|
|
4517
4553
|
}
|
|
4518
|
-
if (shapes.length === 0)
|
|
4519
|
-
if (shapes.length > MAX_SKETCH_SHAPES)
|
|
4520
|
-
const labels = /* @__PURE__ */ new
|
|
4554
|
+
if (shapes.length === 0) problems.push("the sketch has no shape");
|
|
4555
|
+
if (shapes.length > MAX_SKETCH_SHAPES) problems.push(`the sketch has ${shapes.length} shapes; it holds at most ${MAX_SKETCH_SHAPES}`);
|
|
4556
|
+
const labels = /* @__PURE__ */ new Set();
|
|
4557
|
+
const twice = /* @__PURE__ */ new Set();
|
|
4558
|
+
const sound = [];
|
|
4521
4559
|
for (const [index, shape] of shapes.entries()) {
|
|
4522
4560
|
const problem = shapeProblem(shape, index, canvas);
|
|
4523
|
-
if (problem !== null)
|
|
4561
|
+
if (problem !== null) {
|
|
4562
|
+
problems.push(problem);
|
|
4563
|
+
continue;
|
|
4564
|
+
}
|
|
4565
|
+
sound.push(index);
|
|
4524
4566
|
const key = shape.label.trim().toLowerCase();
|
|
4525
|
-
if (labels.has(key)
|
|
4526
|
-
|
|
4567
|
+
if (labels.has(key) && !twice.has(key)) {
|
|
4568
|
+
twice.add(key);
|
|
4569
|
+
problems.push(`"${shape.label.trim()}" is the label of two shapes`);
|
|
4570
|
+
}
|
|
4571
|
+
labels.add(key);
|
|
4527
4572
|
}
|
|
4528
|
-
|
|
4573
|
+
const overlapping = /* @__PURE__ */ new Set();
|
|
4574
|
+
for (const index of sound) {
|
|
4575
|
+
const shape = shapes[index];
|
|
4529
4576
|
for (let y = shape.y; y < shape.y + shape.height; y += 1) {
|
|
4530
4577
|
for (let x = shape.x; x < shape.x + shape.width; x += 1) {
|
|
4531
4578
|
if (!inShape(shape, x + 0.5, y + 0.5)) continue;
|
|
4532
|
-
const other
|
|
4533
|
-
|
|
4579
|
+
for (const other of sound) {
|
|
4580
|
+
if (other >= index || !inShape(shapes[other], x + 0.5, y + 0.5)) continue;
|
|
4581
|
+
const pair = `"${shapes[other]?.label.trim()}" and "${shape.label.trim()}" overlap`;
|
|
4582
|
+
if (!overlapping.has(pair)) {
|
|
4583
|
+
overlapping.add(pair);
|
|
4584
|
+
problems.push(pair);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4534
4587
|
}
|
|
4535
4588
|
}
|
|
4536
4589
|
}
|
|
4537
|
-
return
|
|
4590
|
+
return problems;
|
|
4538
4591
|
}
|
|
4539
4592
|
function sentShape(shape) {
|
|
4540
4593
|
const caption = shape.caption?.trim() ?? "";
|
|
@@ -6555,7 +6608,7 @@ const move = {
|
|
|
6555
6608
|
);
|
|
6556
6609
|
}
|
|
6557
6610
|
};
|
|
6558
|
-
const version = "0.
|
|
6611
|
+
const version = "0.55.0";
|
|
6559
6612
|
const manifest = {
|
|
6560
6613
|
version
|
|
6561
6614
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelvibing/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.0",
|
|
4
4
|
"description": "PixelVibing: pixel art game assets from the command line, and the Skills that teach coding assistants to make them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://pixelvibing.com",
|
|
@@ -22,7 +22,9 @@ pixel-vibing-cli generate icons --names "<name>,<name>,..." --purpose <skill|ite
|
|
|
22
22
|
"icon_size": 32, "instruction": "<theme>", "enrich": false, "max_percent": 40 }
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each icon
|
|
25
|
+
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each icon as pixel art, and
|
|
26
|
+
`pull <job id> --hd` lands them as drawn (HD), beside them; `get_asset` with `"version": "hd"` reads
|
|
27
|
+
them so.
|
|
26
28
|
|
|
27
29
|
## Writing it
|
|
28
30
|
|
|
@@ -30,7 +30,10 @@ The layout goes in the call itself, as `canvas` and `shapes`, with the same fiel
|
|
|
30
30
|
"reference_images": ["<image id>"], "max_percent": 40 }
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each piece
|
|
33
|
+
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each piece as pixel art, and
|
|
34
|
+
`pull <job id> --hd` lands them as drawn (HD), beside them; `get_asset` with `"version": "hd"` reads
|
|
35
|
+
them so. A UI drawn with `--no-pixel-art` is HD alone. A shape nothing was drawn for is named in the
|
|
36
|
+
result's `missing`: generate the UI again for it rather than retrying the whole set blind.
|
|
34
37
|
|
|
35
38
|
## Writing it
|
|
36
39
|
|