@pixelvibing/cli 0.60.0 → 0.62.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 +68 -43
- package/package.json +3 -2
- package/skills/pixelvibing-concept-art/SKILL.md +18 -22
- package/skills/pixelvibing-creature/SKILL.md +8 -5
- package/skills/pixelvibing-icons/SKILL.md +10 -9
- package/skills/pixelvibing-object/SKILL.md +8 -8
- package/skills/pixelvibing-sound/SKILL.md +20 -16
- package/skills/pixelvibing-ui/SKILL.md +13 -6
package/dist/pixel-vibing-cli.js
CHANGED
|
@@ -1001,7 +1001,7 @@ class DesktopSession {
|
|
|
1001
1001
|
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
1002
1002
|
const back = await this.awaitCallback(
|
|
1003
1003
|
(port) => this.options.openExternal(
|
|
1004
|
-
`${this.options.apiUrl}/auth/${encodeURIComponent(provider)}/start?desktop=${port}&challenge=${challenge}${this.options.app === "
|
|
1004
|
+
`${this.options.apiUrl}/auth/${encodeURIComponent(provider)}/start?desktop=${port}&challenge=${challenge}${this.options.app === void 0 || this.options.app === "desktop" ? "" : `&app=${this.options.app}`}`
|
|
1005
1005
|
)
|
|
1006
1006
|
);
|
|
1007
1007
|
const outcome = back.code === null ? back.outcome : await this.exchange(back.code, verifier).catch(() => "failed");
|
|
@@ -2764,6 +2764,16 @@ async function writeArtMirror(boundary, art, mirror) {
|
|
|
2764
2764
|
for (const manifest2 of before.manifests) if (!index.manifests.includes(manifest2)) await gone(join(MANIFEST_DIRECTORY, `${manifest2}.json`));
|
|
2765
2765
|
return index;
|
|
2766
2766
|
}
|
|
2767
|
+
function folderMirror(art) {
|
|
2768
|
+
const boundary = new Boundary([art]);
|
|
2769
|
+
return {
|
|
2770
|
+
state: () => mirrorState(boundary, art),
|
|
2771
|
+
map: (each) => writeMapMirror(boundary, art, each),
|
|
2772
|
+
art: (each) => writeArtMirror(boundary, art, each),
|
|
2773
|
+
folder: () => readFolder(boundary, art),
|
|
2774
|
+
moving: (project) => markMoving(boundary, art, project)
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2767
2777
|
const SECTOR_SIDE = 128;
|
|
2768
2778
|
const MOST_READ = 32;
|
|
2769
2779
|
const MOST_WAIT_SECONDS$1 = 60;
|
|
@@ -2880,15 +2890,7 @@ async function syncArt(deps) {
|
|
|
2880
2890
|
await deps.mirror.art({ revision: art.revision, assets: art.assets, pictures, idMap: art.idMap, references: referenced, styleGuide });
|
|
2881
2891
|
}
|
|
2882
2892
|
function mirrorOf(local) {
|
|
2883
|
-
|
|
2884
|
-
const boundary = new Boundary([art]);
|
|
2885
|
-
return {
|
|
2886
|
-
state: () => mirrorState(boundary, art),
|
|
2887
|
-
map: (each) => writeMapMirror(boundary, art, each),
|
|
2888
|
-
art: (each) => writeArtMirror(boundary, art, each),
|
|
2889
|
-
folder: () => readFolder(boundary, art),
|
|
2890
|
-
moving: (project) => markMoving(boundary, art, project)
|
|
2891
|
-
};
|
|
2893
|
+
return folderMirror(artOf(local));
|
|
2892
2894
|
}
|
|
2893
2895
|
const deflated = promisify(deflate);
|
|
2894
2896
|
const inflated = promisify(inflate);
|
|
@@ -4272,7 +4274,16 @@ function phaserFiles(made) {
|
|
|
4272
4274
|
format: "RGBA8888",
|
|
4273
4275
|
size: { w: page.width, h: page.height },
|
|
4274
4276
|
scale: 1,
|
|
4275
|
-
|
|
4277
|
+
// A frame that stretches carries its centre as `scale9Borders`, which Phaser 4's atlas parser gives the frame, so
|
|
4278
|
+
// `this.add.nineslice(x, y, key, frame, width, height)` stretches it without being told the insets.
|
|
4279
|
+
frames: packed.placements.filter((placed) => placed.page === index).map((placed) => {
|
|
4280
|
+
const grid = made.nineGrids.get(placed.name);
|
|
4281
|
+
return {
|
|
4282
|
+
filename: placed.name,
|
|
4283
|
+
...textureFrame(placed),
|
|
4284
|
+
...grid === void 0 ? {} : { scale9Borders: { x: grid.left, y: grid.top, w: placed.width - grid.left - grid.right, h: placed.height - grid.top - grid.bottom } }
|
|
4285
|
+
};
|
|
4286
|
+
})
|
|
4276
4287
|
})),
|
|
4277
4288
|
meta: { app: "https://pixelvibing.com", version: String(EXPORT_FORMAT) }
|
|
4278
4289
|
};
|
|
@@ -4619,8 +4630,50 @@ function encodeWith(zlib, raster) {
|
|
|
4619
4630
|
function pngCodec(zlib) {
|
|
4620
4631
|
return { decodePng: (bytes) => decodeWith(zlib, bytes), encodePng: (raster) => encodeWith(zlib, raster) };
|
|
4621
4632
|
}
|
|
4622
|
-
const EXPORT_MARK = ".pixelvibing-export";
|
|
4623
4633
|
const codec$1 = pngCodec({ inflate: (data, maxOutputLength) => inflateSync(data, { maxOutputLength }), deflate: (data) => deflateSync(data) });
|
|
4634
|
+
class ExportRefused extends Error {
|
|
4635
|
+
constructor(message) {
|
|
4636
|
+
super(message);
|
|
4637
|
+
this.name = "ExportRefused";
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
const REFUSALS = [ProjectError, PackError, PngError, MapTextError, MirrorError, IdMapError, SyntaxError, ExportRefused];
|
|
4641
|
+
async function exportFolder(art, engine, options = {}) {
|
|
4642
|
+
try {
|
|
4643
|
+
const boundary = new Boundary([art]);
|
|
4644
|
+
const read = await readProjectArt(boundary, art);
|
|
4645
|
+
const mirrored2 = await readMirroredProjectMap(boundary, art);
|
|
4646
|
+
const mapText = mirrored2 === null ? await readProjectMap(boundary, art) : null;
|
|
4647
|
+
checkExportable(
|
|
4648
|
+
read.pictures.map((picture) => {
|
|
4649
|
+
if (!isPng(picture.bytes)) throw new ExportRefused(`the picture ${picture.name} cannot be read: it is not a PNG`);
|
|
4650
|
+
return { name: picture.name, ...pngSize(picture.bytes) };
|
|
4651
|
+
})
|
|
4652
|
+
);
|
|
4653
|
+
const pictures = read.pictures.map((picture) => {
|
|
4654
|
+
try {
|
|
4655
|
+
return { name: picture.name, raster: codec$1.decodePng(picture.bytes) };
|
|
4656
|
+
} catch (error) {
|
|
4657
|
+
throw new ExportRefused(`the picture ${picture.name} cannot be read: ${error.message}`);
|
|
4658
|
+
}
|
|
4659
|
+
});
|
|
4660
|
+
return exportProject(
|
|
4661
|
+
{
|
|
4662
|
+
pictures,
|
|
4663
|
+
entries: read.entries,
|
|
4664
|
+
map: mirrored2 ?? (mapText === null ? null : parseMapText(mapText, "pixelvibing/map.json").document),
|
|
4665
|
+
idMap: read.idMap === null ? null : parseIdMap(JSON.parse(read.idMap))
|
|
4666
|
+
},
|
|
4667
|
+
engine,
|
|
4668
|
+
codec$1,
|
|
4669
|
+
options
|
|
4670
|
+
);
|
|
4671
|
+
} catch (error) {
|
|
4672
|
+
if (REFUSALS.some((kind) => error instanceof kind)) throw error instanceof ExportRefused ? error : new ExportRefused(error.message);
|
|
4673
|
+
throw error;
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
const EXPORT_MARK = ".pixelvibing-export";
|
|
4624
4677
|
async function isDirectory(path) {
|
|
4625
4678
|
try {
|
|
4626
4679
|
return (await lstat(path)).isDirectory();
|
|
@@ -4699,37 +4752,9 @@ const exportCommand = {
|
|
|
4699
4752
|
if (!await replaceable(out)) throw new CliError(`${out} holds files that are not an export's: nothing was written. Name an empty directory with --out`);
|
|
4700
4753
|
let result;
|
|
4701
4754
|
try {
|
|
4702
|
-
|
|
4703
|
-
const read = await readProjectArt(boundary, art);
|
|
4704
|
-
const mirrored2 = await readMirroredProjectMap(boundary, art);
|
|
4705
|
-
const mapText = mirrored2 === null ? await readProjectMap(boundary, art) : null;
|
|
4706
|
-
checkExportable(
|
|
4707
|
-
read.pictures.map((picture) => {
|
|
4708
|
-
if (!isPng(picture.bytes)) throw new CliError(`the picture ${picture.name} cannot be read: it is not a PNG`);
|
|
4709
|
-
return { name: picture.name, ...pngSize(picture.bytes) };
|
|
4710
|
-
})
|
|
4711
|
-
);
|
|
4712
|
-
const pictures = read.pictures.map((picture) => {
|
|
4713
|
-
try {
|
|
4714
|
-
return { name: picture.name, raster: codec$1.decodePng(picture.bytes) };
|
|
4715
|
-
} catch (error) {
|
|
4716
|
-
throw new CliError(`the picture ${picture.name} cannot be read: ${error.message}`);
|
|
4717
|
-
}
|
|
4718
|
-
});
|
|
4719
|
-
result = exportProject(
|
|
4720
|
-
{
|
|
4721
|
-
pictures,
|
|
4722
|
-
entries: read.entries,
|
|
4723
|
-
map: mirrored2 ?? (mapText === null ? null : parseMapText(mapText, "pixelvibing/map.json").document),
|
|
4724
|
-
idMap: read.idMap === null ? null : parseIdMap(JSON.parse(read.idMap))
|
|
4725
|
-
},
|
|
4726
|
-
engine,
|
|
4727
|
-
codec$1,
|
|
4728
|
-
godotRoot === void 0 ? {} : { godotRoot }
|
|
4729
|
-
);
|
|
4755
|
+
result = await exportFolder(art, engine, godotRoot === void 0 ? {} : { godotRoot });
|
|
4730
4756
|
} catch (error) {
|
|
4731
|
-
|
|
4732
|
-
if (refusals.some((kind) => error instanceof kind)) throw new CliError(`nothing was exported: ${error.message}`);
|
|
4757
|
+
if (error instanceof ExportRefused) throw new CliError(`nothing was exported: ${error.message}`);
|
|
4733
4758
|
throw error;
|
|
4734
4759
|
}
|
|
4735
4760
|
await writeWhole(out, result.files, engine);
|
|
@@ -7480,7 +7505,7 @@ function terminalOf(streams) {
|
|
|
7480
7505
|
step: (title, at = "step") => drawStep(streams.stderr, title, at)
|
|
7481
7506
|
};
|
|
7482
7507
|
}
|
|
7483
|
-
const version = "0.
|
|
7508
|
+
const version = "0.62.0";
|
|
7484
7509
|
const manifest = {
|
|
7485
7510
|
version
|
|
7486
7511
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelvibing/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.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",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"build": "npm run build --workspace=@pixelvibing/toolkit && node scripts/build.mjs",
|
|
27
27
|
"check": "node ../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
|
|
28
28
|
"check:package": "node ../editor/src/main/package-guard.ts dist && node ../editor/src/main/package-guard.ts skills",
|
|
29
|
-
"test": "node --import ../editor/scripts/test-register.mjs --test \"src/**/*.test.ts\""
|
|
29
|
+
"test": "node --import ../editor/scripts/test-register.mjs --test \"src/**/*.test.ts\"",
|
|
30
|
+
"skills": "node --import ../editor/scripts/test-register.mjs scripts/skills.mjs"
|
|
30
31
|
},
|
|
31
32
|
"publishConfig": {
|
|
32
33
|
"access": "public"
|
|
@@ -20,6 +20,8 @@ pixel-vibing-cli generate concept-art --instruction "<the picture>" [--size <w>x
|
|
|
20
20
|
pixel-vibing-cli generate concept-art --instruction "<the area>" --pixel-art --tiles <cols>x<rows> [--no-enrich] --yes --json
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
The fields under *Writing it* are these flags: `size` is `--size <w>x<h>`, `background: "transparent"` is `--transparent`, `mask_image` is `--mask <png>`, each tag a `--tag`, each of `reference_images` a `--reference <png>`, `pixel_art` and `tiles` are `--pixel-art --tiles`, and `enrich: false` is `--no-enrich`.
|
|
24
|
+
|
|
23
25
|
## With the MCP server
|
|
24
26
|
|
|
25
27
|
```json
|
|
@@ -37,29 +39,23 @@ which every other generation takes as `reference_images`.
|
|
|
37
39
|
## Writing it
|
|
38
40
|
|
|
39
41
|
- **The instruction.** Brief it as an art director would: what is in the picture, where,
|
|
40
|
-
how it looks, the light, and how its parts meet. Use the style guide's words.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
how it looks, the light, and how its parts meet. Use the style guide's words. It is
|
|
43
|
+
ordinary art, not pixel art, made to draw other generations from: for a new project, make
|
|
44
|
+
one tagged `anchor` first.
|
|
45
|
+
- **The size.** `size` in px, `{ width, height }`, multiples of 16 within 1536×1024 or
|
|
46
|
+
1024×1536, 1536×1024 unless given. `background: "transparent"` draws it on a transparent
|
|
47
|
+
background, for one thing alone: say so in the words too.
|
|
45
48
|
- **Tags** say what it is for, at most 8: `anchor` (the look everything holds to),
|
|
46
|
-
`level-design`, `ui`, `character`, `environment`, `prop`, or one of the project's own
|
|
47
|
-
|
|
48
|
-
- **
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Pass it as `--mask <png>` (MCP: keep it with `upload_image`, then give its id as
|
|
57
|
-
`mask_image`). With `--transparent`, the black part comes back transparent as well.
|
|
58
|
-
- **A level design.** `--pixel-art --tiles <c>x<r>` (MCP `pixel_art`, `tiles`) draws an area
|
|
59
|
-
of the map as pixel art some tiles across, `6x4` unless given, tagged `level-design`; the
|
|
60
|
-
editor takes it apart into objects and terrain.
|
|
61
|
-
- **Enrichment.** Pass `--no-enrich` (`enrich: false`) when the brief above is complete;
|
|
62
|
-
leave it on when all you have is the person's few words.
|
|
49
|
+
`level-design`, `ui`, `character`, `environment`, `prop`, or one of the project's own.
|
|
50
|
+
- **References.** Up to 8 to draw from (`reference_images`), such as the anchor itself.
|
|
51
|
+
- **A mask** (`mask_image`) keeps the drawing to a shape: an island's coastline, a
|
|
52
|
+
character's silhouette, a frame for a menu. It is an opaque picture of black and white
|
|
53
|
+
only, white where the art is drawn and black where it is left empty, in the drawing's
|
|
54
|
+
shape, at most 1536 px a side.
|
|
55
|
+
- **A level design.** `pixel_art: true` with `tiles` draws an area of the map as pixel art
|
|
56
|
+
some tiles across, 6×4 unless given, tagged `level-design`, to be taken apart into objects
|
|
57
|
+
and terrain.
|
|
58
|
+
- **Enrichment.** Turn it off (`enrich: false`) when the instruction above is complete; leave it on when all you have is the person's few words.
|
|
63
59
|
|
|
64
60
|
## After
|
|
65
61
|
|
|
@@ -15,6 +15,8 @@ Load the `pixelvibing` Skill first: who is signed in, the style guide, the limit
|
|
|
15
15
|
pixel-vibing-cli generate creature --instruction "<who>" [--reference <png>]... [--no-enrich] --yes --json
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
The fields under *Writing it* are these flags: each of `reference_images` a `--reference <png>`, such as a concept art pulled under `pixelvibing/ref/`, and `enrich: false` is `--no-enrich`.
|
|
19
|
+
|
|
18
20
|
## With the MCP server
|
|
19
21
|
|
|
20
22
|
```json
|
|
@@ -32,11 +34,12 @@ pixel-vibing-cli generate creature --instruction "<who>" [--reference <png>]...
|
|
|
32
34
|
characters or names, and no text in the picture: when the person names a franchise, make
|
|
33
35
|
original art in its spirit and say so.
|
|
34
36
|
- **One creature, standing.** It is drawn whole, standing, on one tile. A pose, a crowd or a
|
|
35
|
-
scene is concept art
|
|
36
|
-
- **References.** Up to 3: a character sheet or a concept art tagged
|
|
37
|
-
`anchor` keeps it on model.
|
|
38
|
-
- **
|
|
39
|
-
|
|
37
|
+
scene is concept art.
|
|
38
|
+
- **References.** Up to 3 (`reference_images`): a character sheet or a concept art tagged
|
|
39
|
+
`character` or `anchor` keeps it on model.
|
|
40
|
+
- **Its motion.** Its eight directions and its states — walking, hurt, dead — are changes of
|
|
41
|
+
it, made once it exists.
|
|
42
|
+
- **Enrichment.** Turn it off (`enrich: false`) when the instruction above is complete; leave it on when all you have is the person's few words.
|
|
40
43
|
|
|
41
44
|
## After
|
|
42
45
|
|
|
@@ -15,6 +15,8 @@ Load the `pixelvibing` Skill first: who is signed in, the style guide, the limit
|
|
|
15
15
|
pixel-vibing-cli generate icons --names "<name>,<name>,..." --purpose <skill|item> [--size <16|32|64>] [--instruction "<theme>"] [--no-enrich] --yes --json
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
The fields under *Writing it* are these flags: `names` is `--names`, comma-separated, `icon_size` is `--size`, and `enrich: false` is `--no-enrich`.
|
|
19
|
+
|
|
18
20
|
## With the MCP server
|
|
19
21
|
|
|
20
22
|
```json
|
|
@@ -28,17 +30,16 @@ them so.
|
|
|
28
30
|
|
|
29
31
|
## Writing it
|
|
30
32
|
|
|
31
|
-
- **The names.** One per icon, at most 16, none twice: what the skill or the item
|
|
32
|
-
few words ("red health potion", "rusty key"). Make a whole set in one generation,
|
|
33
|
-
icons match.
|
|
33
|
+
- **The names.** One per icon (`names`), at most 16, none twice: what the skill or the item
|
|
34
|
+
is, in a few words ("red health potion", "rusty key"). Make a whole set in one generation,
|
|
35
|
+
so the icons match.
|
|
34
36
|
- **The purpose.** `skill` draws each on a square badge, for a hotbar or a skill tree;
|
|
35
37
|
`item` draws each alone, for an inventory or loot; `item` unless given.
|
|
36
|
-
- **The size.** 16, 32 or 64 px a side, 32 unless given: the size the game shows them at.
|
|
37
|
-
- **The theme.**
|
|
38
|
-
|
|
39
|
-
- **No references.** An icon never carries text or numbers:
|
|
40
|
-
- **Enrichment.**
|
|
41
|
-
on when all you have is the person's few words.
|
|
38
|
+
- **The size.** `icon_size` 16, 32 or 64 px a side, 32 unless given: the size the game shows them at.
|
|
39
|
+
- **The theme.** The instruction is optional: what the icons have in common, their colours,
|
|
40
|
+
their framing, their material. Use the style guide's words.
|
|
41
|
+
- **No references, no text.** An icon never carries text or numbers: the game draws counts and labels.
|
|
42
|
+
- **Enrichment.** Turn it off (`enrich: false`) when the instruction above is complete; leave it on when all you have is the person's few words.
|
|
42
43
|
|
|
43
44
|
## After
|
|
44
45
|
|
|
@@ -15,6 +15,8 @@ Load the `pixelvibing` Skill first: who is signed in, the style guide, the limit
|
|
|
15
15
|
pixel-vibing-cli generate object --instruction "<what>" --size <cols>x<rows> [--reference <png>]... [--no-enrich] --yes --json
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
The fields under *Writing it* are these flags: `size` is `--size`, each of `reference_images` a `--reference <png>`, such as a concept art pulled under `pixelvibing/ref/`, and `enrich: false` is `--no-enrich`.
|
|
19
|
+
|
|
18
20
|
## With the MCP server
|
|
19
21
|
|
|
20
22
|
```json
|
|
@@ -31,14 +33,12 @@ pixel-vibing-cli generate object --instruction "<what>" --size <cols>x<rows> [--
|
|
|
31
33
|
and colours, the details that make it read at its size, and what it is for in the game.
|
|
32
34
|
Use the style guide's words for the look; never restate its camera or outline, the
|
|
33
35
|
platform adds them. No artist's or other game's names, and no text in the picture.
|
|
34
|
-
- **The footprint.**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- **Enrichment.** Pass `--no-enrich` (`enrich: false`) when the instruction above is
|
|
41
|
-
complete; leave it on when all you have is the person's few words.
|
|
36
|
+
- **The footprint.** `size` is how many tiles it stands on, 1×1 unless given: a crate is
|
|
37
|
+
1×1, a cart 2×1, a house 3×3. The platform sizes the drawing to the project's tile size.
|
|
38
|
+
- **References.** Up to 8 pictures to draw from (`reference_images`): a concept art tagged
|
|
39
|
+
`anchor` keeps every object on one look, and a sketch or an earlier object keeps a shape.
|
|
40
|
+
- **One at a time.** Never generate the same object again to fix a detail: change it instead.
|
|
41
|
+
- **Enrichment.** Turn it off (`enrich: false`) when the instruction above is complete; leave it on when all you have is the person's few words.
|
|
42
42
|
|
|
43
43
|
## After
|
|
44
44
|
|
|
@@ -18,6 +18,8 @@ pixel-vibing-cli generate music --instruction "<the piece>" [--seconds <3-600>]
|
|
|
18
18
|
pixel-vibing-cli generate music --plan <plan.json> [--out <file>] --yes --json
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
The fields under *Writing it* are these flags: `seconds` is `--seconds`, `loop` is `--loop`, `instrumental` is `--instrumental`, and a `plan` is a file `--plan` reads.
|
|
22
|
+
|
|
21
23
|
A plan's file is the track section by section, in order:
|
|
22
24
|
|
|
23
25
|
```json
|
|
@@ -50,25 +52,27 @@ with the tool `generate_sound`. `get_job` follows the job and, once done, names
|
|
|
50
52
|
|
|
51
53
|
## Writing it
|
|
52
54
|
|
|
53
|
-
- **A sound effect
|
|
54
|
-
slow", "a coin picked up, bright and short".
|
|
55
|
-
loop seamlessly, for rain, wind or an engine.
|
|
56
|
-
- **Speech
|
|
57
|
-
generation.
|
|
58
|
-
- **Music
|
|
59
|
-
soft accordion". It runs 60 seconds unless
|
|
60
|
-
600. A game's theme, a level's loop or a menu track is asked at its full length: a short
|
|
61
|
-
ends where it stops.
|
|
62
|
-
- **Music section by section.** Give a plan instead of a description when the track has
|
|
63
|
-
that must land in order: an intro, a loop, a boss phase, an ending. Each section is 3
|
|
64
|
-
seconds, with its own styles, styles to avoid, and lyrics for its sung lines
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
- **A sound effect** (`sfx`). What makes the sound and how it feels: "a wooden door creaking
|
|
56
|
+
open, slow", "a coin picked up, bright and short". `seconds` sets its length; `loop` makes
|
|
57
|
+
it loop seamlessly, for rain, wind or an engine.
|
|
58
|
+
- **Speech** (`speech`). The words exactly as they are said, in a `voice`. Keep a line short;
|
|
59
|
+
one line a generation.
|
|
60
|
+
- **Music** (`music`). The mood, the instruments and the tempo: "a calm harbour theme on
|
|
61
|
+
strings and a soft accordion". It runs 60 seconds unless `seconds` says otherwise, from 3
|
|
62
|
+
to 600. A game's theme, a level's loop or a menu track is asked at its full length: a short
|
|
63
|
+
one ends where it stops. `instrumental` makes one with no voice.
|
|
64
|
+
- **Music section by section.** Give a `plan` instead of a description when the track has
|
|
65
|
+
parts that must land in order: an intro, a loop, a boss phase, an ending. Each section is 3
|
|
66
|
+
to 120 seconds, with its own styles, styles to avoid, and lyrics for its sung lines; the
|
|
67
|
+
track is at most 600 seconds in all. `flexible_lengths` lets each section's length move
|
|
68
|
+
while the track's stays.
|
|
67
69
|
- **What music uses.** Music uses the limit by the minute of its length, rounded up: a
|
|
68
|
-
four-minute theme uses four times a one-minute one. Say so before asking for a long track
|
|
69
|
-
keep `max_percent` to what the person agreed.
|
|
70
|
+
four-minute theme uses four times a one-minute one. Say so before asking for a long track.
|
|
70
71
|
- **Original sound.** Never another game's music or theme, and never a real person's voice:
|
|
71
72
|
when the person names one, make original sound in its spirit and say so.
|
|
73
|
+
|
|
74
|
+
## After
|
|
75
|
+
|
|
72
76
|
- **Where it goes.** The game loads the MP3 from where it was written; copy it into the game's
|
|
73
77
|
own audio folder when that is where the engine looks. Sounds are not part of the map, the
|
|
74
78
|
export or the sealed container.
|
|
@@ -19,6 +19,8 @@ and `references/ui.example.json` is a whole HUD laid out.
|
|
|
19
19
|
pixel-vibing-cli generate ui --sketch <file.json> --instruction "<the set, in a line>" [--reference <png>]... --yes --json
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
The fields under *Writing it* are the sketch's: `canvas` and `shapes` are its file's, read by `--sketch`; each of the references a `--reference <png>`; `--no-enrich` and `enrich` are refused for a UI.
|
|
23
|
+
|
|
22
24
|
## With the MCP server
|
|
23
25
|
|
|
24
26
|
The layout goes in the call itself, as `canvas` and `shapes`, with the same fields:
|
|
@@ -39,13 +41,18 @@ result's `missing`: generate the UI again for it rather than retrying the whole
|
|
|
39
41
|
|
|
40
42
|
- **The instruction** is the set's theme in a line, up to 600 characters; what each piece
|
|
41
43
|
is goes in its `label` and `note`.
|
|
42
|
-
- **
|
|
43
|
-
|
|
44
|
-
- **
|
|
44
|
+
- **The canvas.** `canvas` is 128×128, 256×256, 256×128 or 128×256 px: the one the screen's
|
|
45
|
+
pieces fit at the size the game shows them.
|
|
46
|
+
- **The shapes.** 1 to 16, none overlapping, 4 px or more apart. Each has a `label` (its role,
|
|
47
|
+
never drawn, unique), a `kind` (`rect`, `ellipse` or `polygon` with `sides` 3 to 8),
|
|
48
|
+
`x`, `y`, `width` and `height` (at least 4), and as needed a `caption` (the only text
|
|
49
|
+
drawn), a `note` (how it looks), a `border` (its frame, which stretches) and `hollow`
|
|
50
|
+
(only the border drawn).
|
|
51
|
+
- **References**, up to 3, are a style to follow (frames, materials, a way of drawing), never
|
|
52
|
+
what to draw. A concept art tagged `ui` is the usual one.
|
|
53
|
+
- **Text.** Only a caption is drawn as text. Counts, values and names change in play: the
|
|
45
54
|
game draws them.
|
|
46
|
-
- **No enrichment switch.** A UI's layout is read as it is;
|
|
47
|
-
refused for it.
|
|
48
|
-
- `ui-sketch` is the same kind by its platform name.
|
|
55
|
+
- **No enrichment switch.** A UI's layout is read as it is; `enrich` is refused for it.
|
|
49
56
|
|
|
50
57
|
## After
|
|
51
58
|
|