@pixerate/editor 0.260.0 → 0.262.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/agent/index.cjs +19 -3
- package/dist/agent/index.js +21 -5
- package/dist/bridge/cli.cjs +4 -3
- package/dist/bridge/cli.js +3 -2
- package/dist/bridge/index.cjs +4 -3
- package/dist/bridge/index.js +3 -2
- package/dist/{chunk-KBNCEZZB.cjs → chunk-5NRD76T7.cjs} +9 -7
- package/dist/{chunk-SPPDMY7T.js → chunk-6LWEL5DO.js} +4 -2
- package/dist/{chunk-RTPBM46F.cjs → chunk-7TD63DZP.cjs} +1 -294
- package/dist/{chunk-ABHOYBZD.js → chunk-K4BGRCSH.js} +1 -294
- package/dist/chunk-SYEOA5MS.js +301 -0
- package/dist/{chunk-5DLCOQOQ.cjs → chunk-YBC3RKT2.cjs} +177 -16
- package/dist/chunk-ZEKI6EPI.cjs +301 -0
- package/dist/{chunk-UCYW4M66.js → chunk-ZQI3WUPP.js} +163 -2
- package/dist/paint-editor/contract.d.cts +11 -0
- package/dist/paint-editor/contract.d.ts +11 -0
- package/dist/paint-editor/controller.d.cts +17 -0
- package/dist/paint-editor/controller.d.ts +17 -0
- package/dist/paint-editor/index.cjs +245 -84
- package/dist/paint-editor/index.d.cts +2 -1
- package/dist/paint-editor/index.d.ts +2 -1
- package/dist/paint-editor/index.js +163 -2
- package/dist/paint-editor/path-samples.d.cts +19 -0
- package/dist/paint-editor/path-samples.d.ts +19 -0
- package/dist/paint-editor/ui.d.cts +6 -0
- package/dist/paint-editor/ui.d.ts +6 -0
- package/dist/tools/conformance.d.cts +138 -0
- package/dist/tools/conformance.d.ts +138 -0
- package/dist/tools/index.cjs +19 -3
- package/dist/tools/index.d.cts +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +21 -5
- package/package.json +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +2 -0
- package/skills/pixerate-paint-sketch-to-finish/SKILL.md +5 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseSkillFrontmatter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-K4BGRCSH.js";
|
|
4
4
|
import {
|
|
5
5
|
readingTime
|
|
6
6
|
} from "./chunk-EHTX34RE.js";
|
|
@@ -596,6 +596,160 @@ function createOriginMiddleware(options) {
|
|
|
596
596
|
};
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
+
// src/tools/conformance.ts
|
|
600
|
+
var ConformanceIds = class {
|
|
601
|
+
toPlaceholder = /* @__PURE__ */ new Map();
|
|
602
|
+
fromPlaceholder = /* @__PURE__ */ new Map();
|
|
603
|
+
/** The placeholder for `id`, assigning the next one when it is new. */
|
|
604
|
+
add(id) {
|
|
605
|
+
let placeholder = this.toPlaceholder.get(id);
|
|
606
|
+
if (!placeholder) {
|
|
607
|
+
placeholder = `@${this.toPlaceholder.size + 1}`;
|
|
608
|
+
this.toPlaceholder.set(id, placeholder);
|
|
609
|
+
this.fromPlaceholder.set(placeholder, id);
|
|
610
|
+
}
|
|
611
|
+
return placeholder;
|
|
612
|
+
}
|
|
613
|
+
placeholder(id) {
|
|
614
|
+
return this.toPlaceholder.get(id);
|
|
615
|
+
}
|
|
616
|
+
/** `text` with every known id in it replaced by its placeholder (longest ids first). */
|
|
617
|
+
replaceWithin(text) {
|
|
618
|
+
const exact = this.toPlaceholder.get(text);
|
|
619
|
+
if (exact) return exact;
|
|
620
|
+
let out = text;
|
|
621
|
+
for (const id of [...this.toPlaceholder.keys()].sort((a, b) => b.length - a.length)) {
|
|
622
|
+
if (out.includes(id)) out = out.split(id).join(this.toPlaceholder.get(id));
|
|
623
|
+
}
|
|
624
|
+
return out;
|
|
625
|
+
}
|
|
626
|
+
/** The real id a placeholder stands for. */
|
|
627
|
+
resolve(placeholder) {
|
|
628
|
+
return this.fromPlaceholder.get(placeholder);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
var PLACEHOLDER = /^@[1-9][0-9]*$/;
|
|
632
|
+
var TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
633
|
+
var BASE64 = /^[A-Za-z0-9+/=_-]+$/;
|
|
634
|
+
var CONFORMANCE_BINARY_MIN_LENGTH = 200;
|
|
635
|
+
function isBinary(value) {
|
|
636
|
+
return value.startsWith("data:") || value.length >= CONFORMANCE_BINARY_MIN_LENGTH && BASE64.test(value);
|
|
637
|
+
}
|
|
638
|
+
function sortedKeys(value) {
|
|
639
|
+
return Object.keys(value).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
640
|
+
}
|
|
641
|
+
function collectIds(value, ids, isId) {
|
|
642
|
+
if (typeof value === "string") {
|
|
643
|
+
if (isId(value)) ids.add(value);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (Array.isArray(value)) {
|
|
647
|
+
for (const item of value) collectIds(item, ids, isId);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (value && typeof value === "object") {
|
|
651
|
+
for (const k of sortedKeys(value)) {
|
|
652
|
+
if (isId(k)) ids.add(k);
|
|
653
|
+
collectIds(value[k], ids, isId);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
function replaceIds(value, ids) {
|
|
658
|
+
if (typeof value === "string") return isBinary(value) ? "<binary>" : TIMESTAMP.test(value) ? "<time>" : ids.replaceWithin(value);
|
|
659
|
+
if (Array.isArray(value)) return value.map((item) => replaceIds(item, ids));
|
|
660
|
+
if (value && typeof value === "object") {
|
|
661
|
+
const out = {};
|
|
662
|
+
for (const k of sortedKeys(value)) out[ids.replaceWithin(k)] = replaceIds(value[k], ids);
|
|
663
|
+
return out;
|
|
664
|
+
}
|
|
665
|
+
return value;
|
|
666
|
+
}
|
|
667
|
+
function normalizeConformanceValue(value, ids, isGeneratedId = () => false) {
|
|
668
|
+
const json = value === void 0 ? null : JSON.parse(JSON.stringify(value));
|
|
669
|
+
collectIds(json, ids, (v) => !PLACEHOLDER.test(v) && isGeneratedId(v));
|
|
670
|
+
return replaceIds(json, ids);
|
|
671
|
+
}
|
|
672
|
+
function omitConformancePaths(value, paths) {
|
|
673
|
+
let out = value === void 0 ? null : JSON.parse(JSON.stringify(value));
|
|
674
|
+
for (const path of paths) out = omitPath(out, path.split("/").slice(1));
|
|
675
|
+
return out;
|
|
676
|
+
}
|
|
677
|
+
function omitPath(value, parts) {
|
|
678
|
+
if (!value || typeof value !== "object" || parts.length === 0) return value;
|
|
679
|
+
const [head, ...rest] = parts;
|
|
680
|
+
if (Array.isArray(value)) {
|
|
681
|
+
if (head === "*") return value.map((item) => omitPath(item, rest));
|
|
682
|
+
const i = Number(head);
|
|
683
|
+
if (!Number.isInteger(i) || i < 0 || i >= value.length) return value;
|
|
684
|
+
if (rest.length === 0) return value.filter((_, j) => j !== i);
|
|
685
|
+
return value.map((item, j) => j === i ? omitPath(item, rest) : item);
|
|
686
|
+
}
|
|
687
|
+
const object = value;
|
|
688
|
+
const keys = head === "*" ? Object.keys(object) : head in object ? [head] : [];
|
|
689
|
+
for (const key of keys) {
|
|
690
|
+
if (rest.length === 0) delete object[key];
|
|
691
|
+
else object[key] = omitPath(object[key], rest);
|
|
692
|
+
}
|
|
693
|
+
return object;
|
|
694
|
+
}
|
|
695
|
+
function resolveConformanceArguments(value, ids) {
|
|
696
|
+
if (typeof value === "string" && PLACEHOLDER.test(value)) {
|
|
697
|
+
const id = ids.resolve(value);
|
|
698
|
+
if (id === void 0) throw new Error(`Placeholder ${value} is used before any result returned that id.`);
|
|
699
|
+
return id;
|
|
700
|
+
}
|
|
701
|
+
if (Array.isArray(value)) return value.map((item) => resolveConformanceArguments(item, ids));
|
|
702
|
+
if (value && typeof value === "object") {
|
|
703
|
+
const out = {};
|
|
704
|
+
for (const [k, v] of Object.entries(value)) out[k] = resolveConformanceArguments(v, ids);
|
|
705
|
+
return out;
|
|
706
|
+
}
|
|
707
|
+
return value;
|
|
708
|
+
}
|
|
709
|
+
function conformanceResult(result) {
|
|
710
|
+
if (result.isError) {
|
|
711
|
+
const error = result.structuredContent?.error ?? {};
|
|
712
|
+
const value = { code: error.code ?? "tool_failed", message: error.message ?? resultText(result) ?? "", ...error.details !== void 0 ? { details: error.details } : {} };
|
|
713
|
+
return { value: { error: value }, isError: true };
|
|
714
|
+
}
|
|
715
|
+
if (result.structuredContent !== void 0) return { value: result.structuredContent, isError: false };
|
|
716
|
+
const text = resultText(result);
|
|
717
|
+
if (text === void 0) return { value: null, isError: false };
|
|
718
|
+
try {
|
|
719
|
+
return { value: JSON.parse(text), isError: false };
|
|
720
|
+
} catch {
|
|
721
|
+
return { value: text, isError: false };
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function resultText(result) {
|
|
725
|
+
for (const content of result.content) {
|
|
726
|
+
if (content.type === "text" && "text" in content && typeof content.text === "string") return content.text;
|
|
727
|
+
}
|
|
728
|
+
return void 0;
|
|
729
|
+
}
|
|
730
|
+
async function runConformanceScenario(scenario, host) {
|
|
731
|
+
const ids = new ConformanceIds();
|
|
732
|
+
const isId = host.isGeneratedId?.bind(host);
|
|
733
|
+
const outputs = [];
|
|
734
|
+
try {
|
|
735
|
+
for (const step of scenario.steps) {
|
|
736
|
+
const args = resolveConformanceArguments(step.arguments ?? {}, ids);
|
|
737
|
+
const { value, isError } = conformanceResult(await host.callTool(step.tool, args));
|
|
738
|
+
const result = normalizeConformanceValue(value, ids, isId);
|
|
739
|
+
const output = { result: step.ignore ? omitConformancePaths(result, step.ignore) : result, isError };
|
|
740
|
+
if (step.document) output.document = normalizeConformanceValue(host.document(), ids, isId);
|
|
741
|
+
if (step.pixels) {
|
|
742
|
+
const image = host.pixels({ paper: typeof step.pixels === "object" ? step.pixels.paper : void 0 });
|
|
743
|
+
output.pixels = { width: image.width, height: image.height, data: new Uint8Array(image.data.buffer, image.data.byteOffset, image.data.byteLength) };
|
|
744
|
+
}
|
|
745
|
+
outputs.push(output);
|
|
746
|
+
}
|
|
747
|
+
} finally {
|
|
748
|
+
host.dispose?.();
|
|
749
|
+
}
|
|
750
|
+
return outputs;
|
|
751
|
+
}
|
|
752
|
+
|
|
599
753
|
export {
|
|
600
754
|
listSkillEvals,
|
|
601
755
|
imageEvalFixturePixels,
|
|
@@ -611,5 +765,12 @@ export {
|
|
|
611
765
|
runStyleChecks,
|
|
612
766
|
renderStyle,
|
|
613
767
|
createStyleToolProvider,
|
|
614
|
-
createOriginMiddleware
|
|
768
|
+
createOriginMiddleware,
|
|
769
|
+
ConformanceIds,
|
|
770
|
+
CONFORMANCE_BINARY_MIN_LENGTH,
|
|
771
|
+
normalizeConformanceValue,
|
|
772
|
+
omitConformancePaths,
|
|
773
|
+
resolveConformanceArguments,
|
|
774
|
+
conformanceResult,
|
|
775
|
+
runConformanceScenario
|
|
615
776
|
};
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* package version, so a release does not change the files.
|
|
10
10
|
*/
|
|
11
11
|
import type { EffectProvider } from '../effects/types.cjs';
|
|
12
|
+
import type { ConformanceHost } from '../tools/conformance.cjs';
|
|
12
13
|
import type { JsonSchema } from '../tools/schema.cjs';
|
|
14
|
+
import { type PaintEditorOptions } from './controller.cjs';
|
|
13
15
|
/** Bumped when a contract file changes shape (not when its data changes). */
|
|
14
16
|
export declare const PAINT_CONTRACT_FORMAT_VERSION = 1;
|
|
15
17
|
/**
|
|
@@ -27,3 +29,12 @@ export declare const PAINT_DOCUMENT_JSON_SCHEMA: JsonSchema;
|
|
|
27
29
|
export declare function paintContracts(options: {
|
|
28
30
|
effects: EffectProvider;
|
|
29
31
|
}): Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* A fresh paint editor for replaying a conformance scenario (`domains/paint/fixtures`): every paint
|
|
34
|
+
* tool provider in one registry that validates arguments, the saved document (the
|
|
35
|
+
* `pixerate.editor-document` envelope) and the composited pixels. `setup` is the scenario's
|
|
36
|
+
* editor options, such as `{ width, height }`; `effects` is the effect provider the effect tools use.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createPaintConformanceHost(setup: PaintEditorOptions, options: {
|
|
39
|
+
effects: EffectProvider;
|
|
40
|
+
}): ConformanceHost;
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* package version, so a release does not change the files.
|
|
10
10
|
*/
|
|
11
11
|
import type { EffectProvider } from '../effects/types.js';
|
|
12
|
+
import type { ConformanceHost } from '../tools/conformance.js';
|
|
12
13
|
import type { JsonSchema } from '../tools/schema.js';
|
|
14
|
+
import { type PaintEditorOptions } from './controller.js';
|
|
13
15
|
/** Bumped when a contract file changes shape (not when its data changes). */
|
|
14
16
|
export declare const PAINT_CONTRACT_FORMAT_VERSION = 1;
|
|
15
17
|
/**
|
|
@@ -27,3 +29,12 @@ export declare const PAINT_DOCUMENT_JSON_SCHEMA: JsonSchema;
|
|
|
27
29
|
export declare function paintContracts(options: {
|
|
28
30
|
effects: EffectProvider;
|
|
29
31
|
}): Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* A fresh paint editor for replaying a conformance scenario (`domains/paint/fixtures`): every paint
|
|
34
|
+
* tool provider in one registry that validates arguments, the saved document (the
|
|
35
|
+
* `pixerate.editor-document` envelope) and the composited pixels. `setup` is the scenario's
|
|
36
|
+
* editor options, such as `{ width, height }`; `effects` is the effect provider the effect tools use.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createPaintConformanceHost(setup: PaintEditorOptions, options: {
|
|
39
|
+
effects: EffectProvider;
|
|
40
|
+
}): ConformanceHost;
|
|
@@ -26,6 +26,7 @@ import type { ColorScheme } from '../color/dual.cjs';
|
|
|
26
26
|
import { type PaintCanvas, type PaintDocumentState, type PaintLayer } from './document.cjs';
|
|
27
27
|
import { type ImageEncoder, type ImageFormat } from './encode.cjs';
|
|
28
28
|
import type { PenPoint } from './engine.cjs';
|
|
29
|
+
import { type PathSampleOptions } from './path-samples.cjs';
|
|
29
30
|
import type { Hand } from './input.cjs';
|
|
30
31
|
import { type PaperSettings } from './paper.cjs';
|
|
31
32
|
import { type ViewLayer } from './render.cjs';
|
|
@@ -945,6 +946,22 @@ export declare class PaintEditorController {
|
|
|
945
946
|
paintStroke(init: PaintStrokeInit & {
|
|
946
947
|
points: readonly PenPoint[];
|
|
947
948
|
}): PaintStrokeResult;
|
|
949
|
+
/**
|
|
950
|
+
* Brushes vector path data (SVG `d`, document pixels) with the active brush, or `brushId` /
|
|
951
|
+
* `brush`, as if drawn by hand: one recorded stroke per subpath, sampled every `spacing` px
|
|
952
|
+
* with an optional `pressure` profile along each. All of it is one undo step. A given `seed`
|
|
953
|
+
* seeds the first subpath; the next ones take the following seeds.
|
|
954
|
+
*/
|
|
955
|
+
strokePath(init: Omit<PaintStrokeInit, 'start' | 'copies' | 'load'> & PathSampleOptions & {
|
|
956
|
+
path: string;
|
|
957
|
+
}): {
|
|
958
|
+
layerId: string;
|
|
959
|
+
bounds: RasterRect | null;
|
|
960
|
+
dabs: number;
|
|
961
|
+
strokeIds: string[];
|
|
962
|
+
mixerLoad?: MixerLoad;
|
|
963
|
+
wet?: boolean;
|
|
964
|
+
};
|
|
948
965
|
/** Checks stroke samples (finite x and y, pressure 0..1) and returns them; throws a sentence naming the bad point. */
|
|
949
966
|
checkPoints(points: readonly PenPoint[]): PenPoint[];
|
|
950
967
|
/** Every recorded stroke, oldest first. */
|
|
@@ -26,6 +26,7 @@ import type { ColorScheme } from '../color/dual.js';
|
|
|
26
26
|
import { type PaintCanvas, type PaintDocumentState, type PaintLayer } from './document.js';
|
|
27
27
|
import { type ImageEncoder, type ImageFormat } from './encode.js';
|
|
28
28
|
import type { PenPoint } from './engine.js';
|
|
29
|
+
import { type PathSampleOptions } from './path-samples.js';
|
|
29
30
|
import type { Hand } from './input.js';
|
|
30
31
|
import { type PaperSettings } from './paper.js';
|
|
31
32
|
import { type ViewLayer } from './render.js';
|
|
@@ -945,6 +946,22 @@ export declare class PaintEditorController {
|
|
|
945
946
|
paintStroke(init: PaintStrokeInit & {
|
|
946
947
|
points: readonly PenPoint[];
|
|
947
948
|
}): PaintStrokeResult;
|
|
949
|
+
/**
|
|
950
|
+
* Brushes vector path data (SVG `d`, document pixels) with the active brush, or `brushId` /
|
|
951
|
+
* `brush`, as if drawn by hand: one recorded stroke per subpath, sampled every `spacing` px
|
|
952
|
+
* with an optional `pressure` profile along each. All of it is one undo step. A given `seed`
|
|
953
|
+
* seeds the first subpath; the next ones take the following seeds.
|
|
954
|
+
*/
|
|
955
|
+
strokePath(init: Omit<PaintStrokeInit, 'start' | 'copies' | 'load'> & PathSampleOptions & {
|
|
956
|
+
path: string;
|
|
957
|
+
}): {
|
|
958
|
+
layerId: string;
|
|
959
|
+
bounds: RasterRect | null;
|
|
960
|
+
dabs: number;
|
|
961
|
+
strokeIds: string[];
|
|
962
|
+
mixerLoad?: MixerLoad;
|
|
963
|
+
wet?: boolean;
|
|
964
|
+
};
|
|
948
965
|
/** Checks stroke samples (finite x and y, pressure 0..1) and returns them; throws a sentence naming the bad point. */
|
|
949
966
|
checkPoints(points: readonly PenPoint[]): PenPoint[];
|
|
950
967
|
/** Every recorded stroke, oldest first. */
|