pixelkiln 0.19.0 → 0.20.1
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/PROVIDERS.md +1 -1
- package/README.md +4 -3
- package/dist/cli.js +137 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +143 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1855 -157
- package/dist/index.d.ts +1855 -157
- package/dist/index.js +140 -13
- package/dist/index.js.map +1 -1
- package/docs/AGENTS.md +2 -1
- package/docs/ARCHITECTURE.md +14 -8
- package/docs/CLI.md +4 -2
- package/docs/ENDPOINTS.md +1 -1
- package/docs/GENERATORS.md +4 -2
- package/docs/GETTING_STARTED.md +5 -0
- package/docs/LIBRARY.md +5 -0
- package/docs/MANIFEST.md +52 -0
- package/docs/REVISIONS.md +2 -2
- package/package.json +1 -1
- package/schema/manifest.schema.json +395 -176
- package/skills/pixelkiln/SKILL.md +3 -0
package/PROVIDERS.md
CHANGED
|
@@ -85,7 +85,7 @@ prompt, size, batch, and optional seed inputs PixelKiln may replace.
|
|
|
85
85
|
| PixelKiln generator | `map` stills and atomic `frames` sets |
|
|
86
86
|
| Output | One PNG output node: 1–16 map candidates or 2–64 ordered still frames |
|
|
87
87
|
| Revision input | `image-to-image`, `inpaint`, and user-authored `outpaint` graphs; hashed parent/mask uploads and source comparison are covered. The bundled img2img graph passed a three-strength live smoke but missed the requested winter treatment and lost alpha. |
|
|
88
|
-
| Cost model |
|
|
88
|
+
| Cost model | No provider charge; local compute and hosting are outside PixelKiln's estimate |
|
|
89
89
|
| Reproducibility | Workflow content is hashed; model files, custom-node versions, and runtime settings must still be managed outside PixelKiln |
|
|
90
90
|
| Account lifecycle | Read-only connectivity check; no balance, remote object listing, tagging, or purge |
|
|
91
91
|
| Confidence | Live map generation, four-candidate review, cache-only restore, and refinement on Apple MPS; deterministic coverage for frame submission, ordered review/fetch, shared-grid and palette gating, approval, and packing |
|
package/README.md
CHANGED
|
@@ -183,9 +183,10 @@ packaging layer.
|
|
|
183
183
|
}
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
Styles are namespaces.
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
Styles are namespaces. A variant can `extend` a base style while keeping its own
|
|
187
|
+
output directory, so later parent fixes reach every child without copied config.
|
|
188
|
+
Each style re-derives the same asset ids into separate lock keys. Generator
|
|
189
|
+
choice, reference-image bytes, dimensions, palette, seed, and
|
|
189
190
|
prompt settings participate in deterministic spec identity. A manifest may
|
|
190
191
|
select another provider and pass namespaced `providerOptions`; see
|
|
191
192
|
[Set up PixelLab](./docs/PIXELLAB.md),
|
package/dist/cli.js
CHANGED
|
@@ -1836,7 +1836,7 @@ var StyleImageSchema = z2.object({
|
|
|
1836
1836
|
path: z2.string()
|
|
1837
1837
|
});
|
|
1838
1838
|
var HexColorSchema = z2.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour");
|
|
1839
|
-
var
|
|
1839
|
+
var QualityProfileObjectSchema = z2.object({
|
|
1840
1840
|
/** Output root for approved refined art, relative to the manifest. */
|
|
1841
1841
|
outDir: z2.string().min(1),
|
|
1842
1842
|
/** Closed final palette applied after native-grid recovery, without dithering. */
|
|
@@ -1851,7 +1851,8 @@ var QualityProfileSchema = z2.object({
|
|
|
1851
1851
|
fixerPython: z2.string().min(1).optional(),
|
|
1852
1852
|
/** Playback rate recorded for an ordered frame set. */
|
|
1853
1853
|
fps: z2.number().int().min(1).max(60).optional()
|
|
1854
|
-
}).strict()
|
|
1854
|
+
}).strict();
|
|
1855
|
+
var QualityProfileSchema = QualityProfileObjectSchema.refine(
|
|
1855
1856
|
(profile) => new Set(profile.palette.map((color) => color.replace(/^#/, "").toLowerCase())).size === profile.palette.length,
|
|
1856
1857
|
{ message: "quality palette colors must be unique", path: ["palette"] }
|
|
1857
1858
|
);
|
|
@@ -1880,7 +1881,7 @@ var RevisionSchema = z2.object({
|
|
|
1880
1881
|
});
|
|
1881
1882
|
}
|
|
1882
1883
|
});
|
|
1883
|
-
var
|
|
1884
|
+
var StyleObjectSchema = z2.object({
|
|
1884
1885
|
/** Generation backend for this style. Omit to inherit the manifest default. */
|
|
1885
1886
|
provider: z2.string().min(1).optional(),
|
|
1886
1887
|
// `map` is the default because it is 20-40x cheaper and correct for any
|
|
@@ -1995,7 +1996,8 @@ var StyleSchema = z2.object({
|
|
|
1995
1996
|
tags: z2.array(z2.string()).default([]),
|
|
1996
1997
|
/** Adapter-owned settings, keyed by provider id. */
|
|
1997
1998
|
providerOptions: z2.record(z2.record(z2.unknown())).default({})
|
|
1998
|
-
}).strict()
|
|
1999
|
+
}).strict();
|
|
2000
|
+
var StyleSchema = StyleObjectSchema.refine((s) => !(s.tileFeature && s.styleImages.length), {
|
|
1999
2001
|
message: "tileFeature and styleImages cannot be combined \u2014 a connectable set derives its own tile geometry, so remove one or the other",
|
|
2000
2002
|
path: ["tileFeature"]
|
|
2001
2003
|
}).superRefine((style, ctx) => {
|
|
@@ -2007,6 +2009,14 @@ var StyleSchema = z2.object({
|
|
|
2007
2009
|
});
|
|
2008
2010
|
}
|
|
2009
2011
|
});
|
|
2012
|
+
var InheritedStyleSchema = StyleObjectSchema.partial().extend({
|
|
2013
|
+
extends: z2.string().min(1),
|
|
2014
|
+
/** Children own their destination; sharing a parent's output is never implicit. */
|
|
2015
|
+
outDir: z2.string(),
|
|
2016
|
+
/** A child may override only part of the inherited final-art policy. */
|
|
2017
|
+
quality: QualityProfileObjectSchema.partial().optional()
|
|
2018
|
+
}).strict();
|
|
2019
|
+
var StyleInputSchema = z2.union([StyleSchema, InheritedStyleSchema]);
|
|
2010
2020
|
var AssetSchema = z2.object({
|
|
2011
2021
|
/** The subject. Style wrapping comes from the style's prefix/suffix. */
|
|
2012
2022
|
prompt: z2.string(),
|
|
@@ -2095,6 +2105,13 @@ var ManifestSchema = z2.object({
|
|
|
2095
2105
|
styles: z2.record(StyleSchema),
|
|
2096
2106
|
assets: z2.record(AssetSchema)
|
|
2097
2107
|
}).strict();
|
|
2108
|
+
var ManifestInputSchema = z2.object({
|
|
2109
|
+
$schema: z2.string().optional(),
|
|
2110
|
+
name: z2.string(),
|
|
2111
|
+
provider: z2.string().min(1).default("pixellab"),
|
|
2112
|
+
styles: z2.record(StyleInputSchema),
|
|
2113
|
+
assets: z2.record(AssetSchema)
|
|
2114
|
+
}).strict();
|
|
2098
2115
|
var LockEntrySchema = z2.object({
|
|
2099
2116
|
styleId: z2.string(),
|
|
2100
2117
|
assetId: z2.string(),
|
|
@@ -3670,9 +3687,24 @@ function resolveSpecOutputs(spec, lock, manifestDir) {
|
|
|
3670
3687
|
async function loadManifest(manifestPath) {
|
|
3671
3688
|
const abs = path6.resolve(manifestPath);
|
|
3672
3689
|
if (!existsSync4(abs)) throw new Error(`No manifest at ${abs}`);
|
|
3673
|
-
const
|
|
3690
|
+
const input = ManifestInputSchema.safeParse(JSON.parse(await readFile3(abs, "utf8")));
|
|
3691
|
+
if (!input.success) {
|
|
3692
|
+
const issues = formatManifestIssues(input.error.issues);
|
|
3693
|
+
throw new Error(`Manifest at ${abs} is invalid:
|
|
3694
|
+
${issues}`);
|
|
3695
|
+
}
|
|
3696
|
+
let styles;
|
|
3697
|
+
try {
|
|
3698
|
+
styles = resolveStyleInheritance(input.data.styles);
|
|
3699
|
+
} catch (error) {
|
|
3700
|
+
throw new Error(
|
|
3701
|
+
`Manifest at ${abs} is invalid:
|
|
3702
|
+
${error instanceof Error ? error.message : String(error)}`
|
|
3703
|
+
);
|
|
3704
|
+
}
|
|
3705
|
+
const parsed = ManifestSchema.safeParse({ ...input.data, styles });
|
|
3674
3706
|
if (!parsed.success) {
|
|
3675
|
-
const issues = parsed.error.issues
|
|
3707
|
+
const issues = formatManifestIssues(parsed.error.issues);
|
|
3676
3708
|
throw new Error(`Manifest at ${abs} is invalid:
|
|
3677
3709
|
${issues}`);
|
|
3678
3710
|
}
|
|
@@ -3723,6 +3755,61 @@ ${unknownReferences.map((i) => ` ${i}`).join("\n")}`);
|
|
|
3723
3755
|
}
|
|
3724
3756
|
return { manifest: parsed.data, root: path6.dirname(abs), path: abs };
|
|
3725
3757
|
}
|
|
3758
|
+
function formatManifestIssues(issues) {
|
|
3759
|
+
return issues.flatMap((issue) => {
|
|
3760
|
+
if (issue.code === "invalid_union") {
|
|
3761
|
+
return issue.unionErrors.flatMap((error) => formatManifestIssues(error.issues).split("\n"));
|
|
3762
|
+
}
|
|
3763
|
+
return [` ${issue.path.join(".")}: ${issue.message}`];
|
|
3764
|
+
}).join("\n");
|
|
3765
|
+
}
|
|
3766
|
+
function resolveStyleInheritance(styles) {
|
|
3767
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
3768
|
+
const visit = (styleId, chain) => {
|
|
3769
|
+
const cached = resolved.get(styleId);
|
|
3770
|
+
if (cached) return cached;
|
|
3771
|
+
const cycleStart = chain.indexOf(styleId);
|
|
3772
|
+
if (cycleStart >= 0) {
|
|
3773
|
+
throw new Error(
|
|
3774
|
+
`styles.${styleId}.extends: inheritance cycle ${[...chain.slice(cycleStart), styleId].join(" -> ")}`
|
|
3775
|
+
);
|
|
3776
|
+
}
|
|
3777
|
+
const style = styles[styleId];
|
|
3778
|
+
if (!style) throw new Error(`styles.${styleId}: unknown style`);
|
|
3779
|
+
if (!("extends" in style)) {
|
|
3780
|
+
resolved.set(styleId, style);
|
|
3781
|
+
return style;
|
|
3782
|
+
}
|
|
3783
|
+
const parentId = style.extends;
|
|
3784
|
+
if (!Object.hasOwn(styles, parentId)) {
|
|
3785
|
+
throw new Error(`styles.${styleId}.extends: unknown style "${parentId}"`);
|
|
3786
|
+
}
|
|
3787
|
+
const parent = visit(parentId, [...chain, styleId]);
|
|
3788
|
+
const { extends: _parent, ...child } = style;
|
|
3789
|
+
const merged = {
|
|
3790
|
+
...parent,
|
|
3791
|
+
...child,
|
|
3792
|
+
quality: child.quality == null ? parent.quality : { ...parent.quality ?? {}, ...child.quality },
|
|
3793
|
+
providerOptions: mergeProviderOptions(parent.providerOptions, child.providerOptions)
|
|
3794
|
+
};
|
|
3795
|
+
const parsed = StyleSchema.safeParse(merged);
|
|
3796
|
+
if (!parsed.success) {
|
|
3797
|
+
const issues = parsed.error.issues.map((issue) => `styles.${styleId}.${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
3798
|
+
throw new Error(issues);
|
|
3799
|
+
}
|
|
3800
|
+
resolved.set(styleId, parsed.data);
|
|
3801
|
+
return parsed.data;
|
|
3802
|
+
};
|
|
3803
|
+
return Object.fromEntries(Object.keys(styles).map((styleId) => [styleId, visit(styleId, [])]));
|
|
3804
|
+
}
|
|
3805
|
+
function mergeProviderOptions(parent, child) {
|
|
3806
|
+
if (!child) return parent;
|
|
3807
|
+
const merged = { ...parent };
|
|
3808
|
+
for (const [provider, options] of Object.entries(child)) {
|
|
3809
|
+
merged[provider] = { ...parent[provider] ?? {}, ...options };
|
|
3810
|
+
}
|
|
3811
|
+
return merged;
|
|
3812
|
+
}
|
|
3726
3813
|
async function resolveSpecs(loaded, filter) {
|
|
3727
3814
|
const { manifest, root } = loaded;
|
|
3728
3815
|
const providerOverride = filter?.provider;
|
|
@@ -6899,7 +6986,10 @@ function renderSheet(groups) {
|
|
|
6899
6986
|
.loop { display:flex; align-items:flex-start; gap:12px; margin-bottom:12px; }
|
|
6900
6987
|
.loop img { image-rendering:pixelated; border:2px solid var(--accent); padding:7px;
|
|
6901
6988
|
background:var(--panel-deep); max-width:min(var(--preview),calc(100vw - 62px)); height:auto; }
|
|
6902
|
-
.loop-copy { color:var(--dim); font-size:11px; max-width:32ch;
|
|
6989
|
+
.loop-copy { color:var(--dim); font-size:11px; max-width:32ch; display:flex;
|
|
6990
|
+
flex-direction:column; gap:8px; }
|
|
6991
|
+
.loop-copy p { margin:0; }
|
|
6992
|
+
.loop-copy button { align-self:flex-start; padding:5px 9px; font-size:10.5px; }
|
|
6903
6993
|
.cand { border:2px solid var(--line-strong); border-radius:0; padding:7px; background:var(--panel-deep);
|
|
6904
6994
|
cursor:pointer; display:flex; flex:0 0 auto; flex-direction:column; align-items:center; gap:5px;
|
|
6905
6995
|
position:relative; max-width:100%; }
|
|
@@ -7003,15 +7093,49 @@ GROUPS.forEach((g, gi) => {
|
|
|
7003
7093
|
preview.alt = 'Animated frame-set preview';
|
|
7004
7094
|
const copy = document.createElement('div');
|
|
7005
7095
|
copy.className = 'loop-copy';
|
|
7006
|
-
|
|
7096
|
+
const copyText = document.createElement('p');
|
|
7097
|
+
copyText.textContent = g.frameUrls.length + ' ordered frames \xB7 ' + (g.fps || 12) +
|
|
7007
7098
|
' fps. Accepting keeps every frame; one bad frame means leave the set unchosen.';
|
|
7099
|
+
const playback = document.createElement('button');
|
|
7100
|
+
playback.type = 'button';
|
|
7101
|
+
let userPaused = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
7102
|
+
let loopVisible = true;
|
|
7103
|
+
let pageVisible = !document.hidden;
|
|
7104
|
+
let loopTimer = null;
|
|
7105
|
+
let loopIndex = 0;
|
|
7106
|
+
const stopLoop = () => {
|
|
7107
|
+
if (loopTimer !== null) window.clearInterval(loopTimer);
|
|
7108
|
+
loopTimer = null;
|
|
7109
|
+
};
|
|
7110
|
+
const syncLoop = () => {
|
|
7111
|
+
stopLoop();
|
|
7112
|
+
playback.textContent = userPaused ? 'Play preview' : 'Pause preview';
|
|
7113
|
+
playback.setAttribute('aria-pressed', String(userPaused));
|
|
7114
|
+
if (!userPaused && loopVisible && pageVisible) {
|
|
7115
|
+
loopTimer = window.setInterval(() => {
|
|
7116
|
+
loopIndex = (loopIndex + 1) % g.frameUrls.length;
|
|
7117
|
+
preview.src = g.frameUrls[loopIndex];
|
|
7118
|
+
}, Math.max(16, Math.round(1000 / (g.fps || 12))));
|
|
7119
|
+
}
|
|
7120
|
+
};
|
|
7121
|
+
playback.addEventListener('click', () => {
|
|
7122
|
+
userPaused = !userPaused;
|
|
7123
|
+
syncLoop();
|
|
7124
|
+
});
|
|
7125
|
+
copy.append(copyText, playback);
|
|
7008
7126
|
loop.append(preview, copy);
|
|
7009
7127
|
review.insertBefore(loop, frames);
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7128
|
+
document.addEventListener('visibilitychange', () => {
|
|
7129
|
+
pageVisible = !document.hidden;
|
|
7130
|
+
syncLoop();
|
|
7131
|
+
});
|
|
7132
|
+
syncLoop();
|
|
7133
|
+
if ('IntersectionObserver' in window) {
|
|
7134
|
+
new IntersectionObserver((entries) => {
|
|
7135
|
+
loopVisible = entries[0]?.isIntersecting ?? false;
|
|
7136
|
+
syncLoop();
|
|
7137
|
+
}).observe(loop);
|
|
7138
|
+
}
|
|
7015
7139
|
}
|
|
7016
7140
|
g.frameUrls.forEach((url, i) => {
|
|
7017
7141
|
const c = document.createElement('button');
|