sketchmark 2.0.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.
Files changed (64) hide show
  1. package/README.md +188 -0
  2. package/bin/sketchmark.cjs +2008 -0
  3. package/dist/src/builders/index.d.ts +74 -0
  4. package/dist/src/builders/index.js +230 -0
  5. package/dist/src/compounds.d.ts +13 -0
  6. package/dist/src/compounds.js +118 -0
  7. package/dist/src/deck.d.ts +4 -0
  8. package/dist/src/deck.js +91 -0
  9. package/dist/src/diagnostics.d.ts +5 -0
  10. package/dist/src/diagnostics.js +113 -0
  11. package/dist/src/export/index.d.ts +8 -0
  12. package/dist/src/export/index.js +15 -0
  13. package/dist/src/index.d.ts +19 -0
  14. package/dist/src/index.js +35 -0
  15. package/dist/src/kernel.d.ts +8 -0
  16. package/dist/src/kernel.js +68 -0
  17. package/dist/src/normalize.d.ts +6 -0
  18. package/dist/src/normalize.js +191 -0
  19. package/dist/src/patch.d.ts +5 -0
  20. package/dist/src/patch.js +72 -0
  21. package/dist/src/path-sampling.d.ts +3 -0
  22. package/dist/src/path-sampling.js +275 -0
  23. package/dist/src/player/index.d.ts +68 -0
  24. package/dist/src/player/index.js +600 -0
  25. package/dist/src/project.d.ts +11 -0
  26. package/dist/src/project.js +107 -0
  27. package/dist/src/render/html.d.ts +2 -0
  28. package/dist/src/render/html.js +13 -0
  29. package/dist/src/render/raw-three.d.ts +7 -0
  30. package/dist/src/render/raw-three.js +17 -0
  31. package/dist/src/render/svg.d.ts +3 -0
  32. package/dist/src/render/svg.js +277 -0
  33. package/dist/src/render/three-html.d.ts +2 -0
  34. package/dist/src/render/three-html.js +303 -0
  35. package/dist/src/render/three-preview-svg.d.ts +3 -0
  36. package/dist/src/render/three-preview-svg.js +102 -0
  37. package/dist/src/scenes.d.ts +4 -0
  38. package/dist/src/scenes.js +25 -0
  39. package/dist/src/schema.d.ts +2 -0
  40. package/dist/src/schema.js +403 -0
  41. package/dist/src/sequences.d.ts +43 -0
  42. package/dist/src/sequences.js +109 -0
  43. package/dist/src/shapes/builtins.d.ts +2 -0
  44. package/dist/src/shapes/builtins.js +429 -0
  45. package/dist/src/shapes/common.d.ts +9 -0
  46. package/dist/src/shapes/common.js +75 -0
  47. package/dist/src/shapes/geometry.d.ts +22 -0
  48. package/dist/src/shapes/geometry.js +166 -0
  49. package/dist/src/shapes/index.d.ts +2 -0
  50. package/dist/src/shapes/index.js +18 -0
  51. package/dist/src/shapes/registry.d.ts +9 -0
  52. package/dist/src/shapes/registry.js +35 -0
  53. package/dist/src/shapes/types.d.ts +34 -0
  54. package/dist/src/shapes/types.js +2 -0
  55. package/dist/src/types.d.ts +439 -0
  56. package/dist/src/types.js +2 -0
  57. package/dist/src/utils.d.ts +25 -0
  58. package/dist/src/utils.js +157 -0
  59. package/dist/src/validate.d.ts +2 -0
  60. package/dist/src/validate.js +434 -0
  61. package/dist/tests/run.d.ts +1 -0
  62. package/dist/tests/run.js +651 -0
  63. package/package.json +52 -0
  64. package/schema/visual.schema.json +930 -0
@@ -0,0 +1,19 @@
1
+ export * from "./types";
2
+ export * from "./validate";
3
+ export * from "./normalize";
4
+ export * from "./kernel";
5
+ export * from "./schema";
6
+ export * from "./patch";
7
+ export * from "./compounds";
8
+ export * from "./project";
9
+ export * from "./scenes";
10
+ export * from "./sequences";
11
+ export * from "./deck";
12
+ export * from "./diagnostics";
13
+ export * from "./render/svg";
14
+ export * from "./render/html";
15
+ export * from "./render/three-html";
16
+ export * from "./render/three-preview-svg";
17
+ export * from "./render/raw-three";
18
+ export * from "./export";
19
+ export * from "./player";
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./validate"), exports);
19
+ __exportStar(require("./normalize"), exports);
20
+ __exportStar(require("./kernel"), exports);
21
+ __exportStar(require("./schema"), exports);
22
+ __exportStar(require("./patch"), exports);
23
+ __exportStar(require("./compounds"), exports);
24
+ __exportStar(require("./project"), exports);
25
+ __exportStar(require("./scenes"), exports);
26
+ __exportStar(require("./sequences"), exports);
27
+ __exportStar(require("./deck"), exports);
28
+ __exportStar(require("./diagnostics"), exports);
29
+ __exportStar(require("./render/svg"), exports);
30
+ __exportStar(require("./render/html"), exports);
31
+ __exportStar(require("./render/three-html"), exports);
32
+ __exportStar(require("./render/three-preview-svg"), exports);
33
+ __exportStar(require("./render/raw-three"), exports);
34
+ __exportStar(require("./export"), exports);
35
+ __exportStar(require("./player"), exports);
@@ -0,0 +1,8 @@
1
+ import type { KernelVisualDocument, ResolvedVisualDocument, ValidationResult, VisualDocument } from "./types";
2
+ import { type NormalizeOptions } from "./normalize";
3
+ export interface LowerOptions extends NormalizeOptions {
4
+ }
5
+ export declare function lowerVisualDocument(document: VisualDocument, options?: LowerOptions): KernelVisualDocument;
6
+ export declare function resolveKernelFrame(document: VisualDocument, time?: number): KernelVisualDocument;
7
+ export declare function lowerResolvedVisualDocument(document: ResolvedVisualDocument): KernelVisualDocument;
8
+ export declare function validateKernelVisualDocument(document: KernelVisualDocument): ValidationResult;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lowerVisualDocument = lowerVisualDocument;
4
+ exports.resolveKernelFrame = resolveKernelFrame;
5
+ exports.lowerResolvedVisualDocument = lowerResolvedVisualDocument;
6
+ exports.validateKernelVisualDocument = validateKernelVisualDocument;
7
+ const normalize_1 = require("./normalize");
8
+ const utils_1 = require("./utils");
9
+ const shapes_1 = require("./shapes");
10
+ const KERNEL_2D_TYPES = new Set(["group", "path", "text", "image", "point"]);
11
+ const KERNEL_3D_TYPES = new Set(["group3d", "mesh3d", "line3d", "text3d", "point3d", "light"]);
12
+ function lowerVisualDocument(document, options = {}) {
13
+ return lowerResolvedVisualDocument((0, normalize_1.normalizeVisualDocument)(document, options));
14
+ }
15
+ function resolveKernelFrame(document, time = 0) {
16
+ return lowerResolvedVisualDocument((0, normalize_1.resolveVisualFrame)(document, time));
17
+ }
18
+ function lowerResolvedVisualDocument(document) {
19
+ return {
20
+ version: document.version,
21
+ canvas: (0, utils_1.clone)(document.canvas),
22
+ imports: cloneOptional(document.imports),
23
+ assets: cloneOptional(document.assets),
24
+ exports: cloneOptional(document.exports),
25
+ elements: lowerElements(document.elements ?? [])
26
+ };
27
+ }
28
+ function validateKernelVisualDocument(document) {
29
+ const issues = [];
30
+ const visit = (element, path) => {
31
+ if (!KERNEL_2D_TYPES.has(element.type) && !KERNEL_3D_TYPES.has(element.type)) {
32
+ issues.push({ path, code: "unsupported_kernel_type", message: `Kernel element type '${element.type}' is not supported.` });
33
+ return;
34
+ }
35
+ if (element.type === "path" && typeof element.d !== "string") {
36
+ issues.push({ path, code: "missing_kernel_path", message: "Kernel path requires string 'd'." });
37
+ }
38
+ if (element.type === "text" && (!isFiniteNumber(element.x) || !isFiniteNumber(element.y))) {
39
+ issues.push({ path, code: "missing_kernel_text_position", message: "Kernel text requires numeric x and y." });
40
+ }
41
+ if (element.type === "image" && (!isFiniteNumber(element.x) || !isFiniteNumber(element.y) || !isFiniteNumber(element.width) || !isFiniteNumber(element.height))) {
42
+ issues.push({ path, code: "missing_kernel_image_box", message: "Kernel image requires numeric x, y, width, and height." });
43
+ }
44
+ if (element.type === "point" && (!isFiniteNumber(element.x) || !isFiniteNumber(element.y))) {
45
+ issues.push({ path, code: "missing_kernel_point", message: "Kernel point requires numeric x and y." });
46
+ }
47
+ if (element.type === "mesh3d" && (!Array.isArray(element.vertices) || !Array.isArray(element.indices))) {
48
+ issues.push({ path, code: "missing_kernel_mesh", message: "Kernel mesh3d requires vertices and indices." });
49
+ }
50
+ if ((element.type === "group" || element.type === "group3d") && Array.isArray(element.children)) {
51
+ element.children.forEach((child, index) => visit(child, `${path}/children/${index}`));
52
+ }
53
+ };
54
+ (document.elements ?? []).forEach((element, index) => visit(element, `/elements/${index}`));
55
+ return { ok: issues.length === 0, issues, warnings: [] };
56
+ }
57
+ function lowerElements(elements) {
58
+ return elements.flatMap((element) => lowerElement(element));
59
+ }
60
+ function lowerElement(element) {
61
+ return (0, shapes_1.lowerAuthoringElement)(element, { lowerElements });
62
+ }
63
+ function cloneOptional(value) {
64
+ return value === undefined ? undefined : (0, utils_1.clone)(value);
65
+ }
66
+ function isFiniteNumber(value) {
67
+ return typeof value === "number" && Number.isFinite(value);
68
+ }
@@ -0,0 +1,6 @@
1
+ import type { ResolvedVisualDocument, VisualDocument } from "./types";
2
+ export interface NormalizeOptions {
3
+ validate?: boolean;
4
+ }
5
+ export declare function normalizeVisualDocument(document: VisualDocument, options?: NormalizeOptions): ResolvedVisualDocument;
6
+ export declare function resolveVisualFrame(document: VisualDocument, time?: number): ResolvedVisualDocument;
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeVisualDocument = normalizeVisualDocument;
4
+ exports.resolveVisualFrame = resolveVisualFrame;
5
+ const path_sampling_1 = require("./path-sampling");
6
+ const shapes_1 = require("./shapes");
7
+ const utils_1 = require("./utils");
8
+ const validate_1 = require("./validate");
9
+ function normalizeVisualDocument(document, options = {}) {
10
+ if (options.validate !== false) {
11
+ const result = (0, validate_1.validateVisualDocument)(document);
12
+ if (!result.ok) {
13
+ const first = result.issues[0];
14
+ throw new Error(first ? `${first.path}: ${first.message}` : "Invalid visual document.");
15
+ }
16
+ }
17
+ const cloned = (0, utils_1.clone)(document);
18
+ const elements = cloned.elements ?? [];
19
+ const resolvedElements = resolveEndpointReferences(elements);
20
+ return { ...cloned, elements: resolvedElements };
21
+ }
22
+ function resolveVisualFrame(document, time = 0) {
23
+ const normalized = normalizeVisualDocument(document);
24
+ const source = (0, utils_1.clone)(document.elements ?? normalized.elements);
25
+ const animated = source.map((element) => resolveElementAnimation(element, time));
26
+ const endpoints = resolveEndpointReferences(animated);
27
+ const followed = endpoints.map((element) => resolveElementFollowers(element, endpoints, time));
28
+ return { ...normalized, elements: followed };
29
+ }
30
+ function resolveEndpointReferences(elements) {
31
+ const map = new Map();
32
+ for (const element of (0, utils_1.flattenElements)(elements)) {
33
+ if (element.id)
34
+ map.set(element.id, element);
35
+ }
36
+ return elements.map((element) => resolveElementEndpoints(element, map));
37
+ }
38
+ function resolveElementEndpoints(element, map) {
39
+ const next = (0, utils_1.clone)(element);
40
+ if ((next.type === "line" || next.type === "arrow" || next.type === "curve") && next.from !== undefined && next.to !== undefined) {
41
+ next.from = resolveEndpoint(next.from, map);
42
+ next.to = resolveEndpoint(next.to, map);
43
+ }
44
+ if (next.type === "group" && Array.isArray(next.children)) {
45
+ next.children = next.children.map((child) => resolveElementEndpoints(child, map));
46
+ }
47
+ return next;
48
+ }
49
+ function resolveEndpoint(endpoint, map) {
50
+ if ((0, utils_1.isPoint2)(endpoint))
51
+ return [endpoint[0], endpoint[1]];
52
+ const { id, anchor } = (0, utils_1.parseReference)(String(endpoint));
53
+ const target = map.get(id);
54
+ const box = target ? (0, utils_1.elementBox)(target) : undefined;
55
+ if (!box)
56
+ return [0, 0];
57
+ return (0, utils_1.anchorPoint)(box, anchor);
58
+ }
59
+ function resolveElementAnimation(element, time) {
60
+ const next = (0, utils_1.clone)(element);
61
+ if (next.animate) {
62
+ for (const [property, animation] of Object.entries(next.animate)) {
63
+ const record = next;
64
+ record[property] = resolveAnimation(animation, time, record[property]);
65
+ }
66
+ delete next.animate;
67
+ }
68
+ if (next.type === "group" && Array.isArray(next.children)) {
69
+ next.children = next.children.map((child) => resolveElementAnimation(child, time));
70
+ }
71
+ return next;
72
+ }
73
+ function resolveElementFollowers(element, elements, time) {
74
+ const next = (0, utils_1.clone)(element);
75
+ if (next.type === "circle" && next.follow) {
76
+ const point = pointOnFollowPath(elements, next.follow, progressValue(next.progress, time));
77
+ if (point) {
78
+ next.cx = point[0];
79
+ next.cy = point[1];
80
+ }
81
+ }
82
+ if (next.type === "group" && Array.isArray(next.children)) {
83
+ next.children = next.children.map((child) => resolveElementFollowers(child, elements, time));
84
+ }
85
+ return next;
86
+ }
87
+ function progressValue(progress, time) {
88
+ if (typeof progress === "number")
89
+ return clamp(progress, 0, 1);
90
+ if (progress && typeof progress === "object")
91
+ return clamp(Number(resolveAnimation(progress, time, progress.from ?? 0)), 0, 1);
92
+ return 0;
93
+ }
94
+ function resolveAnimation(animation, time, fallback) {
95
+ if (Array.isArray(animation.keyframes) && animation.keyframes.length) {
96
+ const frames = animation.keyframes.slice().sort((left, right) => left[0] - right[0]);
97
+ if (time <= frames[0][0])
98
+ return frames[0][1];
99
+ for (let index = 1; index < frames.length; index += 1) {
100
+ const prev = frames[index - 1];
101
+ const next = frames[index];
102
+ if (time <= next[0]) {
103
+ const span = Math.max(0.000001, next[0] - prev[0]);
104
+ const t = (time - prev[0]) / span;
105
+ return interpolateValue(prev[1], next[1], t);
106
+ }
107
+ }
108
+ return frames[frames.length - 1][1];
109
+ }
110
+ const from = animation.from ?? fallback;
111
+ const to = animation.to ?? from;
112
+ const delay = animation.delay ?? 0;
113
+ const duration = Math.max(0.000001, animation.duration ?? 0);
114
+ const t = (0, utils_1.easing)(animation.ease, (time - delay) / duration);
115
+ return interpolateValue(from ?? 0, to ?? from ?? 0, t);
116
+ }
117
+ function pointOnFollowPath(elements, id, progress) {
118
+ const target = (0, utils_1.flattenElements)(elements).find((element) => element.id === id);
119
+ if (!target)
120
+ return undefined;
121
+ const definition = (0, shapes_1.getInternalShapeDefinition)(target.type);
122
+ if (!definition?.followable)
123
+ return undefined;
124
+ const lowered = lowerFollowTarget(target);
125
+ const path = firstKernelPath(lowered);
126
+ return path ? (0, path_sampling_1.pointOnPath)(path.d, progress) : undefined;
127
+ }
128
+ function lowerFollowTarget(target) {
129
+ const lowerElements = (items) => items.flatMap((item) => (0, shapes_1.lowerAuthoringElement)(item, { lowerElements }));
130
+ return lowerElements([target]);
131
+ }
132
+ function firstKernelPath(elements) {
133
+ for (const element of elements) {
134
+ if (element.type === "path")
135
+ return element;
136
+ if ((element.type === "group" || element.type === "group3d") && Array.isArray(element.children)) {
137
+ const nested = firstKernelPath(element.children);
138
+ if (nested)
139
+ return nested;
140
+ }
141
+ }
142
+ return undefined;
143
+ }
144
+ function interpolateValue(from, to, t) {
145
+ const eased = clamp(t, 0, 1);
146
+ if (typeof from === "number" && typeof to === "number")
147
+ return from + (to - from) * eased;
148
+ if (typeof from === "string" && typeof to === "string") {
149
+ const fromColor = parseHexColor(from);
150
+ const toColor = parseHexColor(to);
151
+ if (fromColor && toColor)
152
+ return formatHexColor({
153
+ r: Math.round(fromColor.r + (toColor.r - fromColor.r) * eased),
154
+ g: Math.round(fromColor.g + (toColor.g - fromColor.g) * eased),
155
+ b: Math.round(fromColor.b + (toColor.b - fromColor.b) * eased),
156
+ a: fromColor.a + (toColor.a - fromColor.a) * eased
157
+ });
158
+ }
159
+ return eased < 1 ? from : to;
160
+ }
161
+ function parseHexColor(value) {
162
+ const input = value.trim();
163
+ const short = /^#([0-9a-f]{3}|[0-9a-f]{4})$/i.exec(input);
164
+ if (short) {
165
+ const chars = short[1];
166
+ return {
167
+ r: parseInt(chars[0] + chars[0], 16),
168
+ g: parseInt(chars[1] + chars[1], 16),
169
+ b: parseInt(chars[2] + chars[2], 16),
170
+ a: chars.length === 4 ? parseInt(chars[3] + chars[3], 16) / 255 : 1
171
+ };
172
+ }
173
+ const long = /^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(input);
174
+ if (!long)
175
+ return undefined;
176
+ const hex = long[1];
177
+ return {
178
+ r: parseInt(hex.slice(0, 2), 16),
179
+ g: parseInt(hex.slice(2, 4), 16),
180
+ b: parseInt(hex.slice(4, 6), 16),
181
+ a: hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1
182
+ };
183
+ }
184
+ function formatHexColor(color) {
185
+ const hex = (value) => clamp(Math.round(value), 0, 255).toString(16).padStart(2, "0");
186
+ const alpha = clamp(Math.round(color.a * 255), 0, 255);
187
+ return alpha >= 255 ? `#${hex(color.r)}${hex(color.g)}${hex(color.b)}` : `#${hex(color.r)}${hex(color.g)}${hex(color.b)}${hex(alpha)}`;
188
+ }
189
+ function clamp(value, min, max) {
190
+ return Math.max(min, Math.min(max, value));
191
+ }
@@ -0,0 +1,5 @@
1
+ import type { VisualDocument, VisualPatchOperation } from "./types";
2
+ export interface PatchResult {
3
+ document: VisualDocument;
4
+ }
5
+ export declare function applyVisualPatch(document: VisualDocument, operations: VisualPatchOperation | VisualPatchOperation[]): PatchResult;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyVisualPatch = applyVisualPatch;
4
+ const utils_1 = require("./utils");
5
+ const validate_1 = require("./validate");
6
+ function applyVisualPatch(document, operations) {
7
+ const ops = Array.isArray(operations) ? operations : [operations];
8
+ const next = (0, utils_1.clone)(document);
9
+ next.elements = next.elements ?? [];
10
+ for (const operation of ops) {
11
+ applyOperation(next, operation);
12
+ }
13
+ const result = (0, validate_1.validateVisualDocument)(next);
14
+ if (!result.ok) {
15
+ const first = result.issues[0];
16
+ throw new Error(first ? `${first.path}: ${first.message}` : "Patch produced an invalid document.");
17
+ }
18
+ return { document: next };
19
+ }
20
+ function applyOperation(document, operation) {
21
+ const elements = document.elements ?? [];
22
+ if (operation.op === "add") {
23
+ if (operation.element.id && findElement(elements, operation.element.id))
24
+ throw new Error(`Element '${operation.element.id}' already exists.`);
25
+ const index = clampIndex(operation.index ?? elements.length, elements.length);
26
+ elements.splice(index, 0, (0, utils_1.clone)(operation.element));
27
+ return;
28
+ }
29
+ const target = "id" in operation ? findElement(elements, operation.id) : undefined;
30
+ if (!target)
31
+ throw new Error(`Element '${"id" in operation ? operation.id : ""}' was not found.`);
32
+ if (operation.op === "update") {
33
+ Object.assign(target.element, (0, utils_1.clone)(operation.set));
34
+ return;
35
+ }
36
+ if (operation.op === "remove") {
37
+ target.parent.splice(target.index, 1);
38
+ return;
39
+ }
40
+ if (operation.op === "replace") {
41
+ target.parent[target.index] = (0, utils_1.clone)(operation.element);
42
+ return;
43
+ }
44
+ if (operation.op === "move") {
45
+ const record = target.element;
46
+ for (const key of ["x", "y", "cx", "cy"]) {
47
+ if (typeof operation[key] === "number")
48
+ record[key] = operation[key];
49
+ }
50
+ return;
51
+ }
52
+ if (operation.op === "reorder") {
53
+ const [item] = target.parent.splice(target.index, 1);
54
+ target.parent.splice(clampIndex(operation.index, target.parent.length), 0, item);
55
+ }
56
+ }
57
+ function findElement(elements, id) {
58
+ for (let index = 0; index < elements.length; index += 1) {
59
+ const element = elements[index];
60
+ if (element.id === id)
61
+ return { element, parent: elements, index };
62
+ if (element.type === "group" && Array.isArray(element.children)) {
63
+ const found = findElement(element.children, id);
64
+ if (found)
65
+ return found;
66
+ }
67
+ }
68
+ return undefined;
69
+ }
70
+ function clampIndex(index, length) {
71
+ return Math.max(0, Math.min(length, Math.floor(index)));
72
+ }
@@ -0,0 +1,3 @@
1
+ import type { Point2 } from "./types";
2
+ export declare function pointOnPath(d: string, progress: number): Point2 | undefined;
3
+ export declare function samplePath(d: string): Point2[];