pixelkiln 0.82.0 → 0.83.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.
@@ -887,6 +887,23 @@
887
887
  setTimeout(() => id.focus(), 0);
888
888
  return form;
889
889
  }
890
+ function revisionModesFor(item) {
891
+ if (item.provider !== "pixellab") return [["image-to-image", "edit with a text instruction"]];
892
+ if (item.outputs.length > 1) {
893
+ return [
894
+ ["edit-animation", "one text edit across every frame"],
895
+ ["reduce-colors", "fewer colours, across every frame"],
896
+ ["correct-pixelart", "clean up the pixel grid, across every frame"]
897
+ ];
898
+ }
899
+ return [
900
+ ["image-to-image", "edit with a text instruction"],
901
+ ["animate", "animate it from a text description"],
902
+ ["animate-pixminimax", "animate it, holding the facing direction"],
903
+ ["reduce-colors", "fewer colours, optional dithering"],
904
+ ["correct-pixelart", "clean up the pixel grid"]
905
+ ];
906
+ }
890
907
  function newRevisionForm(item) {
891
908
  const pr = projectOf(item);
892
909
  const form = el("form", "edit");
@@ -897,18 +914,83 @@
897
914
  id.required = true;
898
915
  id.autocomplete = "off";
899
916
  id.pattern = "[^\\/\\\\]+";
917
+ const modes = revisionModesFor(item);
918
+ const mode = el("select");
919
+ for (const [value, label] of modes) mode.append(new Option(value + ": " + label, value));
900
920
  const prompt = el("textarea");
901
- prompt.placeholder = 'Edit instruction, e.g. "add snow on the roof".';
902
- prompt.required = true;
903
921
  const strength = el("input");
904
922
  strength.type = "number";
905
923
  strength.min = "0";
906
924
  strength.max = "1";
907
925
  strength.step = "0.05";
908
926
  strength.placeholder = "provider default";
927
+ const frames = el("input");
928
+ frames.type = "number";
929
+ frames.min = "4";
930
+ frames.max = "40";
931
+ frames.step = "2";
932
+ frames.placeholder = "provider default";
933
+ const fps = el("input");
934
+ fps.type = "number";
935
+ fps.min = "1";
936
+ fps.max = "60";
937
+ fps.step = "1";
938
+ fps.placeholder = "playback rate";
939
+ const enhance = el("input");
940
+ enhance.type = "checkbox";
941
+ const enhanceField = el("label", "field check");
942
+ enhanceField.append(enhance, el("span", null, "let PixelLab expand the motion description first"));
943
+ const direction = el("select");
944
+ for (const d of ["south", "south-east", "east", "north-east", "north", "north-west", "west", "south-west"]) direction.append(new Option(d, d));
945
+ const numColors = el("input");
946
+ numColors.type = "number";
947
+ numColors.min = "2";
948
+ numColors.max = "256";
949
+ numColors.step = "1";
950
+ numColors.placeholder = "PixelLab default";
951
+ const dithering = el("select");
952
+ for (const d of ["", "none", "2x2", "4x4", "8x8"]) dithering.append(new Option(d || "default", d));
909
953
  const row1 = el("div", "row");
910
- row1.append(field("new asset id", id), field("strength", strength, "PixelLab rejects an explicit strength for image-to-image; leave this blank there."));
911
- form.append(row1, field("edit instruction", prompt, "The style still adds its prefix and suffix."));
954
+ row1.append(field("new asset id", id), field("mode", mode));
955
+ const promptField = field("instruction", prompt);
956
+ const strengthField = field("strength", strength, "PixelLab rejects an explicit strength for image-to-image; leave this blank there. Optional for correct-pixelart.");
957
+ const framesField = field("frames", frames, "Even, 4 to 40; animate allows up to 16.");
958
+ const fpsField = field("fps", fps, "Recorded with the frames; PixelLab does not store one.");
959
+ const directionField = field("facing", direction, "Holds the sprite's facing while the motion is expanded.");
960
+ const colorsField = field("colours", numColors);
961
+ const ditherField = field("dithering", dithering);
962
+ const grids = el("div", "row");
963
+ grids.append(framesField, fpsField);
964
+ const cleanup = el("div", "row");
965
+ cleanup.append(colorsField, ditherField);
966
+ form.append(row1, promptField, strengthField, grids, directionField, enhanceField, cleanup);
967
+ const hints = {
968
+ "image-to-image": ["edit instruction", "add snow on the roof", "The style still adds its prefix and suffix."],
969
+ animate: ["motion", "the chest wobbling gently", "Describes the movement; the parent image is the first frame."],
970
+ "animate-pixminimax": ["motion", "the chest wobbling gently", "Describes the movement; the parent image is the first frame."],
971
+ "edit-animation": ["edit instruction", "add a red cape", "Applied across every frame in one call, so the change stays consistent."],
972
+ "reduce-colors": ["note", "optional", "Cleanup only: the text is kept as a label, not sent."],
973
+ "correct-pixelart": ["note", "optional", "Cleanup only: the text is kept as a label, not sent."]
974
+ };
975
+ const sync = () => {
976
+ const m = mode.value;
977
+ const [label, placeholder, hint] = hints[m];
978
+ promptField.querySelector("span").textContent = label;
979
+ promptField.querySelector("small")?.remove();
980
+ promptField.append(el("small", null, hint));
981
+ prompt.placeholder = placeholder;
982
+ prompt.required = m !== "reduce-colors" && m !== "correct-pixelart";
983
+ strengthField.hidden = m !== "image-to-image" && m !== "correct-pixelart";
984
+ grids.hidden = !(m === "animate" || m === "animate-pixminimax" || m === "edit-animation");
985
+ framesField.hidden = !(m === "animate" || m === "animate-pixminimax");
986
+ fpsField.hidden = grids.hidden;
987
+ directionField.hidden = !(m === "animate-pixminimax" && enhance.checked);
988
+ enhanceField.hidden = !(m === "animate" || m === "animate-pixminimax");
989
+ cleanup.hidden = m !== "reduce-colors";
990
+ };
991
+ mode.onchange = sync;
992
+ enhance.onchange = sync;
993
+ sync();
912
994
  const actions = el("div", "actions");
913
995
  const save = el("button", "primary", "Create revision");
914
996
  save.type = "submit";
@@ -926,9 +1008,19 @@
926
1008
  save.disabled = true;
927
1009
  msg.className = "msg";
928
1010
  msg.textContent = "saving…";
929
- const revision = { mode: "image-to-image", from: item.assetId };
930
- if (strength.value.trim() !== "") revision.strength = Number(strength.value);
931
- const asset = { prompt: prompt.value, styles: [item.styleId], revision };
1011
+ const m = mode.value;
1012
+ const revision = { mode: m, from: item.assetId };
1013
+ if ((m === "image-to-image" || m === "correct-pixelart") && strength.value.trim() !== "") revision.strength = Number(strength.value);
1014
+ if ((m === "animate" || m === "animate-pixminimax") && frames.value.trim() !== "") revision.frames = Number(frames.value);
1015
+ if ((m === "animate" || m === "animate-pixminimax" || m === "edit-animation") && fps.value.trim() !== "") revision.fps = Number(fps.value);
1016
+ if ((m === "animate" || m === "animate-pixminimax") && enhance.checked) revision.enhancePrompt = true;
1017
+ if (m === "animate-pixminimax" && enhance.checked) revision.direction = direction.value;
1018
+ if (m === "reduce-colors") {
1019
+ if (numColors.value.trim() !== "") revision.numColors = Number(numColors.value);
1020
+ if (dithering.value) revision.dithering = dithering.value;
1021
+ }
1022
+ const text = prompt.value.trim() || (m === "reduce-colors" ? "reduce colours" : m === "correct-pixelart" ? "correct pixel art" : "");
1023
+ const asset = { prompt: text, styles: [item.styleId], revision };
932
1024
  try {
933
1025
  const body = { action: "add-asset", assetId: id.value.trim(), expectedSha256: pr.manifestSha256, asset };
934
1026
  if (item.project) body.project = item.project;
@@ -4373,6 +4465,12 @@
4373
4465
  if (item.revision.maskSha256) row(dl, "mask sha256", item.revision.maskSha256.slice(0, 16) + "…", { mono: true, copy: item.revision.maskSha256 });
4374
4466
  if (item.revision.strength !== void 0) row(dl, "strength", item.revision.strength);
4375
4467
  }
4468
+ const declared = item.asset && item.asset.revision;
4469
+ if (declared) {
4470
+ for (const [label, key] of [["facing", "direction"], ["looks like", "description"], ["body template", "skeletonTemplate"], ["frames", "frames"], ["fps", "fps"], ["colours", "numColors"], ["dithering", "dithering"]]) {
4471
+ if (declared[key] !== void 0) row(dl, label, declared[key]);
4472
+ }
4473
+ }
4376
4474
  if (item.skeleton) {
4377
4475
  row(dl, "keypoints", item.skeleton.keypointsFile, { mono: true });
4378
4476
  if (item.skeleton.set) {
package/dist/index.cjs CHANGED
@@ -15450,10 +15450,10 @@ var NewAssetSchema = import_zod8.z.object({
15450
15450
  styles: import_zod8.z.array(import_zod8.z.string().min(1)).optional(),
15451
15451
  /**
15452
15452
  * A controlled regeneration of another asset in the same manifest,
15453
- * instead of a fresh generation. `inpaint` needs a mask upload the
15454
- * gallery does not offer yet, so only `image-to-image` and
15455
- * `animate-skeleton` (whose keypoints file the gallery writes first) can
15456
- * be created here; hand-edit the manifest for the other modes.
15453
+ * instead of a fresh generation. `inpaint` needs a mask upload and
15454
+ * `interpolate` an ending-keyframe image the gallery does not offer, so
15455
+ * those two are hand-edited; every other mode can be created here
15456
+ * (`animate-skeleton` after the gallery writes its keypoints file).
15457
15457
  */
15458
15458
  revision: import_zod8.z.discriminatedUnion("mode", [
15459
15459
  import_zod8.z.object({
@@ -15462,6 +15462,29 @@ var NewAssetSchema = import_zod8.z.object({
15462
15462
  from: import_zod8.z.string().min(1),
15463
15463
  strength: import_zod8.z.number().min(0).max(1).optional()
15464
15464
  }).strict(),
15465
+ import_zod8.z.object({
15466
+ mode: import_zod8.z.literal("reduce-colors"),
15467
+ from: import_zod8.z.string().min(1),
15468
+ /** A target colour count; the gallery leaves PixelLab's default when this is omitted. */
15469
+ numColors: import_zod8.z.number().int().min(2).max(256).optional(),
15470
+ dithering: RevisionDitheringSchema.optional(),
15471
+ ditheringStrength: import_zod8.z.number().min(0).max(10).optional()
15472
+ }).strict(),
15473
+ import_zod8.z.object({ mode: import_zod8.z.literal("correct-pixelart"), from: import_zod8.z.string().min(1), strength: import_zod8.z.number().min(0).max(1).optional() }).strict(),
15474
+ import_zod8.z.object({
15475
+ mode: import_zod8.z.enum(["animate", "animate-pixminimax"]),
15476
+ from: import_zod8.z.string().min(1),
15477
+ frames: import_zod8.z.number().int().min(4).max(40).refine((n) => n % 2 === 0, "frames must be even").optional(),
15478
+ fps: import_zod8.z.number().int().min(1).max(60).optional(),
15479
+ enhancePrompt: import_zod8.z.boolean().optional(),
15480
+ /** `animate-pixminimax` only; the loader refuses it on `animate`. */
15481
+ direction: CharacterDirectionSchema.optional()
15482
+ }).strict(),
15483
+ import_zod8.z.object({
15484
+ mode: import_zod8.z.literal("edit-animation"),
15485
+ from: import_zod8.z.string().min(1),
15486
+ fps: import_zod8.z.number().int().min(1).max(60).optional()
15487
+ }).strict(),
15465
15488
  import_zod8.z.object({
15466
15489
  mode: import_zod8.z.literal("animate-skeleton"),
15467
15490
  from: import_zod8.z.string().min(1),