@prosopo/icon-order-assets 0.0.0-stage → 0.2.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/.turbo/turbo-build$colon$cjs.log +24 -0
- package/.turbo/turbo-build$colon$tsc.log +14 -0
- package/.turbo/turbo-build.log +25 -0
- package/CHANGELOG.md +21 -0
- package/LICENSE +201 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/background.d.ts +4 -0
- package/dist/background.d.ts.map +1 -0
- package/dist/background.js +87 -0
- package/dist/background.js.map +1 -0
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/background.cjs +87 -0
- package/dist/cjs/compose.cjs +108 -0
- package/dist/cjs/glyphs.cjs +41 -0
- package/dist/cjs/index.cjs +64 -0
- package/dist/cjs/place.cjs +68 -0
- package/dist/cjs/shuffle.cjs +16 -0
- package/dist/compose.d.ts +5 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +103 -0
- package/dist/compose.js.map +1 -0
- package/dist/glyphs.d.ts +15 -0
- package/dist/glyphs.d.ts.map +1 -0
- package/dist/glyphs.js +39 -0
- package/dist/glyphs.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/place.d.ts +8 -0
- package/dist/place.d.ts.map +1 -0
- package/dist/place.js +68 -0
- package/dist/place.js.map +1 -0
- package/dist/shuffle.d.ts +3 -0
- package/dist/shuffle.d.ts.map +1 -0
- package/dist/shuffle.js +16 -0
- package/dist/shuffle.js.map +1 -0
- package/dist/tests/iconOrderAssets.unit.test.d.ts +2 -0
- package/dist/tests/iconOrderAssets.unit.test.d.ts.map +1 -0
- package/dist/tests/iconOrderAssets.unit.test.js +202 -0
- package/dist/tests/iconOrderAssets.unit.test.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -3
- package/scripts/preview.ts +73 -0
- package/vite.cjs.config.ts +23 -0
- package/vite.esm.config.ts +22 -0
- package/vite.test.config.ts +32 -0
- package/README.md +0 -3
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createPrng } from "@prosopo/puzzle-assets";
|
|
2
|
+
import sharp from "sharp";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { compositeIcons } from "../compose.js";
|
|
5
|
+
import { GLYPH_KINDS, GlyphKind, glyphPath } from "../glyphs.js";
|
|
6
|
+
import { DEFAULT_GEOMETRY, DEFAULT_RENDER_SETTINGS, LEGEND_ICON_SIZE, gradeClicks, renderIconOrder, } from "../index.js";
|
|
7
|
+
import { placeIcons } from "../place.js";
|
|
8
|
+
const seed = (byte) => Buffer.alloc(16, byte);
|
|
9
|
+
const target = (over = {}) => ({
|
|
10
|
+
x: 100,
|
|
11
|
+
y: 100,
|
|
12
|
+
kind: GlyphKind.ring,
|
|
13
|
+
rotation: 0,
|
|
14
|
+
hue: 200,
|
|
15
|
+
size: 40,
|
|
16
|
+
...over,
|
|
17
|
+
});
|
|
18
|
+
describe("glyphs", () => {
|
|
19
|
+
it("has path data for every kind in the vocabulary", () => {
|
|
20
|
+
for (const kind of GLYPH_KINDS) {
|
|
21
|
+
expect(glyphPath(kind).length).toBeGreaterThan(0);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
it("exposes every enum member as a kind", () => {
|
|
25
|
+
expect([...GLYPH_KINDS].sort()).toEqual(Object.values(GlyphKind).sort());
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("placeIcons", () => {
|
|
29
|
+
it("returns the requested number of targets and decoys", () => {
|
|
30
|
+
const { targets, decoys } = placeIcons(createPrng(seed(7)), DEFAULT_GEOMETRY, 3, 4);
|
|
31
|
+
expect(targets).toHaveLength(3);
|
|
32
|
+
expect(decoys).toHaveLength(4);
|
|
33
|
+
});
|
|
34
|
+
it("gives every icon on the frame a distinct glyph, so the legend is unambiguous", () => {
|
|
35
|
+
const { targets, decoys } = placeIcons(createPrng(seed(11)), DEFAULT_GEOMETRY, 3, 5);
|
|
36
|
+
const kinds = [...targets, ...decoys].map((icon) => icon.kind);
|
|
37
|
+
expect(new Set(kinds).size).toBe(kinds.length);
|
|
38
|
+
});
|
|
39
|
+
it("keeps whole icons inside the frame", () => {
|
|
40
|
+
const { targets, decoys } = placeIcons(createPrng(seed(3)), DEFAULT_GEOMETRY, 3, 4);
|
|
41
|
+
for (const icon of [...targets, ...decoys]) {
|
|
42
|
+
expect(icon.x).toBeGreaterThanOrEqual(0);
|
|
43
|
+
expect(icon.y).toBeGreaterThanOrEqual(0);
|
|
44
|
+
expect(icon.x).toBeLessThanOrEqual(DEFAULT_GEOMETRY.width);
|
|
45
|
+
expect(icon.y).toBeLessThanOrEqual(DEFAULT_GEOMETRY.height);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
it("separates icons so no click can fall inside two hit radii", () => {
|
|
49
|
+
const { targets, decoys } = placeIcons(createPrng(seed(23)), DEFAULT_GEOMETRY, 3, 4);
|
|
50
|
+
const icons = [...targets, ...decoys];
|
|
51
|
+
for (let i = 0; i < icons.length; i++) {
|
|
52
|
+
for (let j = i + 1; j < icons.length; j++) {
|
|
53
|
+
const a = icons[i];
|
|
54
|
+
const b = icons[j];
|
|
55
|
+
if (!a || !b)
|
|
56
|
+
throw new Error("icon underflow");
|
|
57
|
+
expect(Math.hypot(a.x - b.x, a.y - b.y)).toBeGreaterThanOrEqual(1.15 * Math.max(a.size, b.size));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
it("throws rather than shortening the answer when a target cannot be placed", () => {
|
|
62
|
+
const cramped = {
|
|
63
|
+
width: 60,
|
|
64
|
+
height: 60,
|
|
65
|
+
iconSize: 50,
|
|
66
|
+
};
|
|
67
|
+
expect(() => placeIcons(createPrng(seed(5)), cramped, 3, 0)).toThrow(/could not place target icon/);
|
|
68
|
+
});
|
|
69
|
+
it("drops decoys rather than failing when the frame is crowded", () => {
|
|
70
|
+
const cramped = {
|
|
71
|
+
width: 120,
|
|
72
|
+
height: 90,
|
|
73
|
+
iconSize: 34,
|
|
74
|
+
};
|
|
75
|
+
const { targets, decoys } = placeIcons(createPrng(seed(9)), cramped, 2, 6);
|
|
76
|
+
expect(targets).toHaveLength(2);
|
|
77
|
+
expect(decoys.length).toBeLessThan(6);
|
|
78
|
+
});
|
|
79
|
+
it("refuses to ask for more distinct glyphs than exist", () => {
|
|
80
|
+
expect(() => placeIcons(createPrng(seed(1)), DEFAULT_GEOMETRY, 3, GLYPH_KINDS.length)).toThrow(/only \d+ exist/);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe("gradeClicks", () => {
|
|
84
|
+
const targets = [
|
|
85
|
+
target({ x: 50, y: 50 }),
|
|
86
|
+
target({ x: 150, y: 60, kind: GlyphKind.star }),
|
|
87
|
+
target({ x: 240, y: 150, kind: GlyphKind.bolt }),
|
|
88
|
+
];
|
|
89
|
+
const tolerance = 0.6;
|
|
90
|
+
it("accepts exact clicks in the right order", () => {
|
|
91
|
+
expect(gradeClicks(targets, [
|
|
92
|
+
{ x: 50, y: 50 },
|
|
93
|
+
{ x: 150, y: 60 },
|
|
94
|
+
{ x: 240, y: 150 },
|
|
95
|
+
], tolerance)).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
it("accepts clicks inside the hit radius", () => {
|
|
98
|
+
expect(gradeClicks(targets, [
|
|
99
|
+
{ x: 58, y: 55 },
|
|
100
|
+
{ x: 143, y: 66 },
|
|
101
|
+
{ x: 246, y: 143 },
|
|
102
|
+
], tolerance)).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
it("rejects the right icons clicked in the wrong order", () => {
|
|
105
|
+
expect(gradeClicks(targets, [
|
|
106
|
+
{ x: 150, y: 60 },
|
|
107
|
+
{ x: 50, y: 50 },
|
|
108
|
+
{ x: 240, y: 150 },
|
|
109
|
+
], tolerance)).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
it("rejects a click outside the hit radius", () => {
|
|
112
|
+
expect(gradeClicks(targets, [
|
|
113
|
+
{ x: 50, y: 50 },
|
|
114
|
+
{ x: 150, y: 60 },
|
|
115
|
+
{ x: 240, y: 190 },
|
|
116
|
+
], tolerance)).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
it("rejects too few and too many clicks", () => {
|
|
119
|
+
expect(gradeClicks(targets, [
|
|
120
|
+
{ x: 50, y: 50 },
|
|
121
|
+
{ x: 150, y: 60 },
|
|
122
|
+
], tolerance)).toBe(false);
|
|
123
|
+
expect(gradeClicks(targets, [
|
|
124
|
+
{ x: 50, y: 50 },
|
|
125
|
+
{ x: 150, y: 60 },
|
|
126
|
+
{ x: 240, y: 150 },
|
|
127
|
+
{ x: 240, y: 150 },
|
|
128
|
+
], tolerance)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
it("scales the hit radius with the icon, so jittered sizes are equally hittable", () => {
|
|
131
|
+
const small = [target({ x: 100, y: 100, size: 20 })];
|
|
132
|
+
const large = [target({ x: 100, y: 100, size: 60 })];
|
|
133
|
+
const click = [{ x: 118, y: 100 }];
|
|
134
|
+
expect(gradeClicks(small, click, 0.6)).toBe(false);
|
|
135
|
+
expect(gradeClicks(large, click, 0.6)).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
describe("the collage background", () => {
|
|
139
|
+
const edgeEnergy = (image) => {
|
|
140
|
+
const { data, width, height } = image;
|
|
141
|
+
const channels = 4;
|
|
142
|
+
let total = 0;
|
|
143
|
+
let count = 0;
|
|
144
|
+
for (let y = 0; y < height; y++) {
|
|
145
|
+
for (let x = 1; x < width; x++) {
|
|
146
|
+
const i = (y * width + x) * channels;
|
|
147
|
+
const prev = i - channels;
|
|
148
|
+
for (let c = 0; c < 3; c++) {
|
|
149
|
+
total += Math.abs((data[i + c] ?? 0) - (data[prev + c] ?? 0));
|
|
150
|
+
count++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return total / count;
|
|
155
|
+
};
|
|
156
|
+
it("puts more edges on the frame as clutter rises", async () => {
|
|
157
|
+
const frameAt = (backgroundClutter) => compositeIcons(createPrng(seed(7)), [], DEFAULT_GEOMETRY, {
|
|
158
|
+
...DEFAULT_RENDER_SETTINGS,
|
|
159
|
+
backgroundClutter,
|
|
160
|
+
});
|
|
161
|
+
const [plain, busy] = await Promise.all([frameAt(0), frameAt(20)]);
|
|
162
|
+
expect(edgeEnergy(busy)).toBeGreaterThan(edgeEnergy(plain));
|
|
163
|
+
});
|
|
164
|
+
it("still renders at zero clutter, as the operator escape hatch", async () => {
|
|
165
|
+
const challenge = await renderIconOrder(DEFAULT_GEOMETRY, {
|
|
166
|
+
...DEFAULT_RENDER_SETTINGS,
|
|
167
|
+
backgroundClutter: 0,
|
|
168
|
+
});
|
|
169
|
+
const meta = await sharp(challenge.background).metadata();
|
|
170
|
+
expect(meta.width).toBe(DEFAULT_GEOMETRY.width);
|
|
171
|
+
expect(challenge.targets).toHaveLength(DEFAULT_RENDER_SETTINGS.targetCount);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe("renderIconOrder", () => {
|
|
175
|
+
it("renders a frame and a legend sized to the target count", async () => {
|
|
176
|
+
const challenge = await renderIconOrder();
|
|
177
|
+
const background = await sharp(challenge.background).metadata();
|
|
178
|
+
expect(background.width).toBe(DEFAULT_GEOMETRY.width);
|
|
179
|
+
expect(background.height).toBe(DEFAULT_GEOMETRY.height);
|
|
180
|
+
const legend = await sharp(challenge.legend).metadata();
|
|
181
|
+
expect(legend.height).toBe(LEGEND_ICON_SIZE);
|
|
182
|
+
expect(legend.width).toBe(DEFAULT_RENDER_SETTINGS.targetCount * (LEGEND_ICON_SIZE + 6) - 6);
|
|
183
|
+
expect(challenge.targets).toHaveLength(DEFAULT_RENDER_SETTINGS.targetCount);
|
|
184
|
+
expect(challenge.legendIconSize).toBe(LEGEND_ICON_SIZE);
|
|
185
|
+
});
|
|
186
|
+
it("produces a different frame every call, so a background is never reused", async () => {
|
|
187
|
+
const [a, b] = await Promise.all([renderIconOrder(), renderIconOrder()]);
|
|
188
|
+
expect(a.background.equals(b.background)).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
it("honours the target and decoy counts it is given", async () => {
|
|
191
|
+
const settings = {
|
|
192
|
+
...DEFAULT_RENDER_SETTINGS,
|
|
193
|
+
targetCount: 4,
|
|
194
|
+
decoyCount: 2,
|
|
195
|
+
};
|
|
196
|
+
const challenge = await renderIconOrder(DEFAULT_GEOMETRY, settings);
|
|
197
|
+
expect(challenge.targets).toHaveLength(4);
|
|
198
|
+
const legend = await sharp(challenge.legend).metadata();
|
|
199
|
+
expect(legend.width).toBe(4 * (LEGEND_ICON_SIZE + 6) - 6);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=iconOrderAssets.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iconOrderAssets.unit.test.js","sourceRoot":"","sources":["../../src/tests/iconOrderAssets.unit.test.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { GlyphKind } from "./glyphs.js";
|
|
2
|
+
export interface IconOrderGeometry {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
iconSize: number;
|
|
6
|
+
}
|
|
7
|
+
export interface IconPlacement {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
kind: GlyphKind;
|
|
11
|
+
rotation: number;
|
|
12
|
+
hue: number;
|
|
13
|
+
size: number;
|
|
14
|
+
}
|
|
15
|
+
export interface RenderedIconOrder {
|
|
16
|
+
background: Buffer;
|
|
17
|
+
legend: Buffer;
|
|
18
|
+
legendIconSize: number;
|
|
19
|
+
targets: IconPlacement[];
|
|
20
|
+
}
|
|
21
|
+
export interface IconOrderRenderSettings {
|
|
22
|
+
targetCount: number;
|
|
23
|
+
decoyCount: number;
|
|
24
|
+
strokeWidth: number;
|
|
25
|
+
iconOpacity: number;
|
|
26
|
+
haloOpacity: number;
|
|
27
|
+
backgroundClutter: number;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IAEf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAGD,MAAM,WAAW,aAAa;IAE7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,SAAS,CAAC;IAEhB,QAAQ,EAAE,MAAM,CAAC;IAEjB,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAEjC,UAAU,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE,MAAM,CAAC;IAEf,cAAc,EAAE,MAAM,CAAC;IAEvB,OAAO,EAAE,aAAa,EAAE,CAAC;CACzB;AAGD,MAAM,WAAW,uBAAuB;IAEvC,WAAW,EAAE,MAAM,CAAC;IAEpB,UAAU,EAAE,MAAM,CAAC;IAEnB,WAAW,EAAE,MAAM,CAAC;IAEpB,WAAW,EAAE,MAAM,CAAC;IAEpB,WAAW,EAAE,MAAM,CAAC;IAEpB,iBAAiB,EAAE,MAAM,CAAC;CAC1B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/icon-order-assets",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Procedural imagery for the ordered-icon selection captcha",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": "^24"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/cjs/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/prosopo/captcha.git",
|
|
21
|
+
"directory": "packages/icon-order-assets"
|
|
22
|
+
},
|
|
23
|
+
"author": "Prosopo Limited",
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/prosopo/captcha/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/prosopo/captcha#readme",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@prosopo/puzzle-assets": "0.2.1",
|
|
31
|
+
"sharp": "0.35.4"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@prosopo/config": "3.4.3",
|
|
35
|
+
"@types/node": "22.10.2",
|
|
36
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
37
|
+
"del-cli": "6.0.0",
|
|
38
|
+
"dotenv": "16.4.5",
|
|
39
|
+
"tslib": "2.7.0",
|
|
40
|
+
"tsx": "4.20.3",
|
|
41
|
+
"typescript": "5.6.2",
|
|
42
|
+
"vite": "8.1.5",
|
|
43
|
+
"vitest": "4.1.11"
|
|
44
|
+
},
|
|
45
|
+
"sideEffects": false,
|
|
46
|
+
"scripts": {
|
|
47
|
+
"clean": "del-cli --verbose dist tsconfig.tsbuildinfo",
|
|
48
|
+
"build": "pnpm run build:cross-env --mode ${NODE_ENV:-development}",
|
|
49
|
+
"build:cross-env": "vite build --config vite.esm.config.ts",
|
|
50
|
+
"build:tsc": "tsc --build --verbose",
|
|
51
|
+
"build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
|
|
52
|
+
"preview": "tsx scripts/preview.ts",
|
|
53
|
+
"test": "NODE_ENV=${NODE_ENV:-test}; pnpm exec vitest run --config ./vite.test.config.ts",
|
|
54
|
+
"typecheck": "tsc --project tsconfig.types.json"
|
|
55
|
+
}
|
|
6
56
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { encodePng } from "@prosopo/puzzle-assets";
|
|
18
|
+
import sharp from "sharp";
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_GEOMETRY,
|
|
21
|
+
DEFAULT_RENDER_SETTINGS,
|
|
22
|
+
renderIconOrder,
|
|
23
|
+
} from "../src/index.js";
|
|
24
|
+
|
|
25
|
+
/** Writes sample challenges to `preview/` as PNG, so codec artefacts don't hide generator output. */
|
|
26
|
+
const main = async (): Promise<void> => {
|
|
27
|
+
const outDir = path.resolve("preview");
|
|
28
|
+
await mkdir(outDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < 4; i++) {
|
|
31
|
+
const challenge = await renderIconOrder(
|
|
32
|
+
DEFAULT_GEOMETRY,
|
|
33
|
+
DEFAULT_RENDER_SETTINGS,
|
|
34
|
+
);
|
|
35
|
+
const background = await sharp(challenge.background)
|
|
36
|
+
.ensureAlpha()
|
|
37
|
+
.raw()
|
|
38
|
+
.toBuffer({ resolveWithObject: true });
|
|
39
|
+
const legend = await sharp(challenge.legend)
|
|
40
|
+
.ensureAlpha()
|
|
41
|
+
.raw()
|
|
42
|
+
.toBuffer({ resolveWithObject: true });
|
|
43
|
+
|
|
44
|
+
await writeFile(
|
|
45
|
+
path.join(outDir, `frame-${i}.png`),
|
|
46
|
+
await encodePng({
|
|
47
|
+
data: background.data,
|
|
48
|
+
width: background.info.width,
|
|
49
|
+
height: background.info.height,
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
await writeFile(
|
|
53
|
+
path.join(outDir, `legend-${i}.png`),
|
|
54
|
+
await encodePng({
|
|
55
|
+
data: legend.data,
|
|
56
|
+
width: legend.info.width,
|
|
57
|
+
height: legend.info.height,
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
console.log(
|
|
61
|
+
"frame-%d: targets %s",
|
|
62
|
+
i,
|
|
63
|
+
challenge.targets
|
|
64
|
+
.map((t) => `${t.kind}@${Math.round(t.x)},${Math.round(t.y)}`)
|
|
65
|
+
.join(" "),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
main().catch((err: unknown) => {
|
|
71
|
+
console.error(err);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { ViteCommonJSConfig } from "@prosopo/config";
|
|
17
|
+
|
|
18
|
+
export default function () {
|
|
19
|
+
return ViteCommonJSConfig(
|
|
20
|
+
path.basename("."),
|
|
21
|
+
path.resolve("./tsconfig.json"),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { ViteEsmConfig } from "@prosopo/config";
|
|
17
|
+
|
|
18
|
+
export default function () {
|
|
19
|
+
return ViteEsmConfig(path.basename("."), path.resolve("./tsconfig.json"), [
|
|
20
|
+
"src/index.ts",
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
import { ViteTestConfig } from "@prosopo/config";
|
|
17
|
+
import dotenv from "dotenv";
|
|
18
|
+
process.env.NODE_ENV = "test";
|
|
19
|
+
// if .env.test exists at this level, use it, otherwise use the one at the root
|
|
20
|
+
const envFile = `.env.${process.env.NODE_ENV || "development"}`;
|
|
21
|
+
let envPath = envFile;
|
|
22
|
+
if (fs.existsSync(envFile)) {
|
|
23
|
+
envPath = path.resolve(envFile);
|
|
24
|
+
} else if (fs.existsSync(`../../${envFile}`)) {
|
|
25
|
+
envPath = path.resolve(`../../${envFile}`);
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error(`No ${envFile} file found`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dotenv.config({ path: envPath });
|
|
31
|
+
|
|
32
|
+
export default ViteTestConfig();
|
package/README.md
DELETED