@ludic-labs/assembly 0.0.0-stage → 0.4.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 (78) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/LICENSE +2 -0
  3. package/README.md +50 -2
  4. package/dist/bake.d.ts +80 -0
  5. package/dist/bake.js +214 -0
  6. package/dist/browser.d.ts +89 -0
  7. package/dist/browser.js +534 -0
  8. package/dist/compat.d.ts +17 -0
  9. package/dist/compat.js +153 -0
  10. package/dist/compiler.d.ts +18 -0
  11. package/dist/compiler.js +1264 -0
  12. package/dist/diagnostics.d.ts +5 -0
  13. package/dist/diagnostics.js +8 -0
  14. package/dist/export.d.ts +54 -0
  15. package/dist/export.js +272 -0
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +3 -0
  18. package/dist/legacy.d.ts +36 -0
  19. package/dist/legacy.js +180 -0
  20. package/dist/math.d.ts +16 -0
  21. package/dist/math.js +62 -0
  22. package/dist/mesh.d.ts +11 -0
  23. package/dist/mesh.js +153 -0
  24. package/dist/motion.d.ts +63 -0
  25. package/dist/motion.js +1476 -0
  26. package/dist/pixel.d.ts +268 -0
  27. package/dist/pixel.js +942 -0
  28. package/dist/required.d.ts +2 -0
  29. package/dist/required.js +6 -0
  30. package/dist/shapes.d.ts +71 -0
  31. package/dist/shapes.js +396 -0
  32. package/dist/sprite-brief.d.ts +5 -0
  33. package/dist/sprite-brief.js +47 -0
  34. package/dist/sprite-compiler.d.ts +62 -0
  35. package/dist/sprite-compiler.js +1745 -0
  36. package/dist/sprite-decompile.d.ts +68 -0
  37. package/dist/sprite-decompile.js +786 -0
  38. package/dist/sprite-format.d.ts +4 -0
  39. package/dist/sprite-format.js +155 -0
  40. package/dist/sprite-guide.d.ts +40 -0
  41. package/dist/sprite-guide.js +330 -0
  42. package/dist/sprite-scene.d.ts +277 -0
  43. package/dist/sprite-scene.js +664 -0
  44. package/dist/sprite-types.d.ts +110 -0
  45. package/dist/sprite-types.js +1 -0
  46. package/dist/sprite.d.ts +8 -0
  47. package/dist/sprite.js +8 -0
  48. package/dist/svg/schema.d.ts +71 -0
  49. package/dist/svg/schema.js +217 -0
  50. package/dist/svg/types.d.ts +148 -0
  51. package/dist/svg/types.js +1 -0
  52. package/dist/svg.d.ts +26 -0
  53. package/dist/svg.js +539 -0
  54. package/dist/text.d.ts +12 -0
  55. package/dist/text.js +75 -0
  56. package/dist/types.d.ts +478 -0
  57. package/dist/types.js +1 -0
  58. package/docs/svg/README.md +150 -0
  59. package/docs/svg/specification.md +108 -0
  60. package/examples/svg/arcane-shield.json +261 -0
  61. package/examples/svg/aurora-ribbon.json +158 -0
  62. package/examples/svg/bouncing-ember.json +76 -0
  63. package/examples/svg/ember-fountain.json +224 -0
  64. package/examples/svg/lantern-keeper.json +1159 -0
  65. package/examples/svg/night-workshop.json +1120 -0
  66. package/examples/svg/waygate-platform.json +64 -0
  67. package/examples/svg/waygate-spark.json +92 -0
  68. package/examples/svg/waygate-wisp.json +105 -0
  69. package/examples/svg/waygate.json +485 -0
  70. package/package.json +132 -4
  71. package/schemas/asset-assembly-0.1.schema.json +354 -0
  72. package/schemas/asset-assembly-0.2.schema.json +429 -0
  73. package/schemas/asset-assembly-0.3.schema.json +572 -0
  74. package/schemas/motion-set-0.1.schema.json +178 -0
  75. package/schemas/pixel-sprite-0.1.schema.json +892 -0
  76. package/schemas/pixel-sprite-scene-0.1.schema.json +149 -0
  77. package/schemas/svg-asset-0.1.schema.json +1157 -0
  78. package/tools/svg-asset.mjs +106 -0
@@ -0,0 +1,110 @@
1
+ /** Loose JSON input. The compiler validates structure and semantics at runtime. */
2
+ export type SpriteDocument = Record<string, unknown>;
3
+ export type SpritePoint = [number, number];
4
+ export type SpriteBox = [number, number, number, number];
5
+ export type SpriteColour = [number, number, number, number];
6
+ export interface SpriteDiagnostic {
7
+ severity: "error" | "warning";
8
+ code: string;
9
+ path: string;
10
+ message: string;
11
+ part?: string;
12
+ received?: unknown;
13
+ expected?: unknown;
14
+ }
15
+ export interface SpriteGuidePart {
16
+ bbox: SpriteBox;
17
+ area: number;
18
+ mask: number[];
19
+ instance?: boolean;
20
+ depth?: number;
21
+ z?: number;
22
+ projected?: SpriteBox;
23
+ subpixel?: boolean;
24
+ }
25
+ export interface SpriteGuide {
26
+ width: number;
27
+ height: number;
28
+ parts: Record<string, SpriteGuidePart>;
29
+ hidden?: string[];
30
+ owners?: Int32Array;
31
+ depth?: Float32Array | Float64Array;
32
+ /** Per pixel, the light tone of the visible surface (/pixel roles: 0 outline, 1 deep, 2 shadow, 3 mid, 4 light; -1 empty). */
33
+ roles?: Int8Array;
34
+ instances?: string[];
35
+ originX?: number;
36
+ originY?: number;
37
+ cut?: string[];
38
+ pixelsPerMetre?: number;
39
+ camera?: string;
40
+ }
41
+ export interface SpriteView {
42
+ camera: string | {
43
+ yaw: number;
44
+ pitch: number;
45
+ };
46
+ pixelsPerMetre: number;
47
+ margin?: number;
48
+ hide?: string[];
49
+ pose?: {
50
+ clip: string;
51
+ phase: number;
52
+ };
53
+ notes?: unknown;
54
+ }
55
+ export interface SpriteRenderSpec {
56
+ camera?: SpriteView["camera"];
57
+ pixelsPerMetre?: number;
58
+ /** Exact occupied row count, taking precedence over pixelsPerMetre. */
59
+ fitHeight?: number;
60
+ margin?: number;
61
+ profile?: string;
62
+ features?: string[];
63
+ snap?: boolean;
64
+ }
65
+ export interface SpriteRenderBase {
66
+ width: number;
67
+ height: number;
68
+ rgba: Uint8Array;
69
+ owners: Int32Array;
70
+ names: string[];
71
+ }
72
+ export interface CompileSpriteOptions {
73
+ renderBase?: (spec: SpriteRenderSpec, path: string) => SpriteRenderBase;
74
+ guide?: (view: SpriteView, path: string) => SpriteGuide;
75
+ }
76
+ export interface SpriteCel {
77
+ id: string;
78
+ canvas: import("./sprite-compiler.js").SpriteCanvas;
79
+ pivot: SpritePoint;
80
+ view: string | null;
81
+ }
82
+ export interface SpriteAnchor {
83
+ path: string;
84
+ part?: string;
85
+ guide?: string;
86
+ point: SpritePoint;
87
+ }
88
+ export interface SpriteGuideReport {
89
+ cel: string;
90
+ part: string;
91
+ guide: SpriteBox;
92
+ authored: SpriteBox | null;
93
+ overlap: number;
94
+ guideArea: number;
95
+ area: number;
96
+ }
97
+ export interface SpriteResult {
98
+ ok: boolean;
99
+ diagnostics: SpriteDiagnostic[];
100
+ parts: string[];
101
+ cels: Map<string, SpriteCel>;
102
+ animations: Map<string, {
103
+ cels: string[];
104
+ ms: number[];
105
+ loop: boolean;
106
+ }>;
107
+ anchors: SpriteAnchor[];
108
+ guides: Map<string, SpriteGuide | null>;
109
+ guideReport: SpriteGuideReport[];
110
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ /** Typed, host-independent pixel sprites and guided scene composition. */
2
+ export * from "./sprite-types.js";
3
+ export * from "./sprite-compiler.js";
4
+ export * from "./sprite-guide.js";
5
+ export * from "./sprite-decompile.js";
6
+ export * from "./sprite-scene.js";
7
+ export * from "./sprite-format.js";
8
+ export * from "./sprite-brief.js";
package/dist/sprite.js ADDED
@@ -0,0 +1,8 @@
1
+ /** Typed, host-independent pixel sprites and guided scene composition. */
2
+ export * from "./sprite-types.js";
3
+ export * from "./sprite-compiler.js";
4
+ export * from "./sprite-guide.js";
5
+ export * from "./sprite-decompile.js";
6
+ export * from "./sprite-scene.js";
7
+ export * from "./sprite-format.js";
8
+ export * from "./sprite-brief.js";
@@ -0,0 +1,71 @@
1
+ /** One structural definition feeds the dependency-free checker and published JSON Schema. */
2
+ export interface Rule {
3
+ $ref?: string;
4
+ type?: string;
5
+ const?: unknown;
6
+ enum?: unknown[];
7
+ oneOf?: Rule[];
8
+ properties?: Record<string, Rule>;
9
+ required?: string[];
10
+ additionalProperties?: boolean | Rule;
11
+ propertyNames?: Rule;
12
+ items?: Rule | false;
13
+ prefixItems?: Rule[];
14
+ minItems?: number;
15
+ maxItems?: number;
16
+ maxProperties?: number;
17
+ minimum?: number;
18
+ maximum?: number;
19
+ exclusiveMinimum?: number;
20
+ minLength?: number;
21
+ maxLength?: number;
22
+ pattern?: string;
23
+ }
24
+ export declare const svgAssetSchema: {
25
+ $ref?: string;
26
+ type?: string;
27
+ const?: unknown;
28
+ enum?: unknown[];
29
+ oneOf?: Rule[];
30
+ properties?: Record<string, Rule>;
31
+ required?: string[];
32
+ additionalProperties?: boolean | Rule;
33
+ propertyNames?: Rule;
34
+ items?: Rule | false;
35
+ prefixItems?: Rule[];
36
+ minItems?: number;
37
+ maxItems?: number;
38
+ maxProperties?: number;
39
+ minimum?: number;
40
+ maximum?: number;
41
+ exclusiveMinimum?: number;
42
+ minLength?: number;
43
+ maxLength?: number;
44
+ pattern?: string;
45
+ $schema: string;
46
+ $id: string;
47
+ title: string;
48
+ description: string;
49
+ $defs: {
50
+ expr: {
51
+ oneOf: Rule[];
52
+ };
53
+ paint: {
54
+ oneOf: Rule[];
55
+ };
56
+ command: {
57
+ oneOf: Rule[];
58
+ };
59
+ shape: {
60
+ oneOf: Rule[];
61
+ };
62
+ node: {
63
+ oneOf: Rule[];
64
+ };
65
+ gradient: {
66
+ oneOf: Rule[];
67
+ };
68
+ };
69
+ };
70
+ /** Only the closed subset used above; this is not a general JSON Schema interpreter. */
71
+ export declare function checkStructure(value: unknown, rule?: Rule, path?: string, depth?: number): string | null;
@@ -0,0 +1,217 @@
1
+ const ref = (name) => ({ $ref: `#/$defs/${name}` });
2
+ const num = (minimum = -1e6, maximum = 1e6) => ({ type: "number", minimum, maximum });
3
+ const text = (maxLength = 120) => ({ type: "string", maxLength, minLength: 1 });
4
+ const id = { ...text(64), pattern: "^[A-Za-z][A-Za-z0-9_-]*$" };
5
+ const color = { type: "string", pattern: "^#[0-9a-fA-F]{6}$" };
6
+ const arr = (items, minItems = 0, maxItems = 512) => ({
7
+ type: "array",
8
+ items,
9
+ minItems,
10
+ maxItems,
11
+ });
12
+ const tuple = (...prefixItems) => ({
13
+ type: "array",
14
+ prefixItems,
15
+ items: false,
16
+ minItems: prefixItems.length,
17
+ maxItems: prefixItems.length,
18
+ });
19
+ const obj = (properties, required = Object.keys(properties)) => ({
20
+ type: "object",
21
+ properties,
22
+ required,
23
+ additionalProperties: false,
24
+ });
25
+ const dict = (value) => ({
26
+ type: "object",
27
+ propertyNames: id,
28
+ additionalProperties: value,
29
+ maxProperties: 128,
30
+ });
31
+ const expr = ref("expr"), point = tuple(expr, expr);
32
+ const base = {
33
+ id,
34
+ transform: obj({ translate: point, rotate: expr, scale: point, pivot: point }, []),
35
+ style: obj({
36
+ fill: ref("paint"),
37
+ stroke: ref("paint"),
38
+ strokeWidth: expr,
39
+ linecap: { enum: ["butt", "round", "square"] },
40
+ linejoin: { enum: ["miter", "round", "bevel"] },
41
+ fillRule: { enum: ["nonzero", "evenodd"] },
42
+ }, []),
43
+ opacity: expr,
44
+ clip: id,
45
+ mask: id,
46
+ blur: expr,
47
+ };
48
+ const node = (kind, fields, required = Object.keys(fields)) => obj({ ...base, kind: { const: kind }, ...fields }, ["id", "kind", ...required]);
49
+ const shapes = [
50
+ node("rect", { x: expr, y: expr, width: expr, height: expr, radius: expr }, ["x", "y", "width", "height"]),
51
+ node("ellipse", { cx: expr, cy: expr, rx: expr, ry: expr }),
52
+ node("path", { d: arr(ref("command"), 1, 2048) }),
53
+ ];
54
+ const count = { type: "integer", minimum: 1, maximum: 1024 };
55
+ const param = obj({ default: num(), min: num(), max: num() });
56
+ const stop = obj({ offset: num(0, 1), color, opacity: num(0, 1) }, ["offset", "color"]);
57
+ export const svgAssetSchema = {
58
+ $schema: "https://json-schema.org/draft/2020-12/schema",
59
+ $id: "https://libraries.ludic-assembly.victorqueiroz.dev/schema/svg-asset-0.1.json",
60
+ title: "SVG Asset Notation 0.1",
61
+ description: "Structural validation. compileSvgAsset additionally validates references, scopes, cycles, key ordering and evaluated geometry.",
62
+ ...obj({
63
+ schema: { const: "svg-asset/0.1" },
64
+ name: text(),
65
+ viewBox: tuple(num(), num(), { ...num(0), exclusiveMinimum: 0 }, { ...num(0), exclusiveMinimum: 0 }),
66
+ seed: { type: "integer", minimum: 0, maximum: 4294967295 },
67
+ params: dict(param),
68
+ palette: dict(color),
69
+ gradients: dict(ref("gradient")),
70
+ clips: dict(arr(ref("shape"), 1)),
71
+ masks: dict(arr(ref("shape"), 1)),
72
+ symbols: dict(obj({ params: dict(param), children: arr(ref("node"), 1) }, ["children"])),
73
+ animations: dict(obj({
74
+ duration: { ...num(0, 60), exclusiveMinimum: 0 },
75
+ loop: { type: "boolean" },
76
+ tracks: dict(arr(obj({ time: num(0, 60), value: num(), ease: { enum: ["linear", "smooth", "hold"] } }, [
77
+ "time",
78
+ "value",
79
+ ]), 2, 256)),
80
+ })),
81
+ defaultAnimation: id,
82
+ nodes: arr(ref("node"), 1),
83
+ }, ["schema", "name", "viewBox", "nodes"]),
84
+ $defs: {
85
+ expr: {
86
+ oneOf: [
87
+ num(),
88
+ { type: "string", pattern: "^\\$[A-Za-z][A-Za-z0-9_-]*$", maxLength: 65 },
89
+ ...[
90
+ ["sin", "cos", "abs"],
91
+ ["add", "sub", "mul", "div", "min", "max"],
92
+ ["clamp", "mix", "rand"],
93
+ ].map((ops, index) => tuple({ enum: ops }, ...Array.from({ length: index + 1 }, () => expr))),
94
+ ],
95
+ },
96
+ paint: {
97
+ oneOf: [
98
+ color,
99
+ { const: "none" },
100
+ { type: "string", pattern: "^@[A-Za-z][A-Za-z0-9_-]*$", maxLength: 65 },
101
+ obj({ gradient: id }),
102
+ ],
103
+ },
104
+ command: {
105
+ oneOf: [
106
+ tuple({ enum: ["M", "L"] }, expr, expr),
107
+ tuple({ const: "Q" }, expr, expr, expr, expr),
108
+ tuple({ const: "C" }, expr, expr, expr, expr, expr, expr),
109
+ tuple({ const: "A" }, expr, expr, expr, { enum: [0, 1] }, { enum: [0, 1] }, expr, expr),
110
+ tuple({ const: "Z" }),
111
+ ],
112
+ },
113
+ shape: { oneOf: shapes },
114
+ node: {
115
+ oneOf: [
116
+ ...shapes,
117
+ node("group", { children: arr(ref("node"), 1) }),
118
+ node("use", { symbol: id, args: dict(expr) }, ["symbol"]),
119
+ node("repeat", { count, children: arr(ref("node"), 1) }),
120
+ node("emitter", {
121
+ count,
122
+ lifetime: { ...num(0, 60), exclusiveMinimum: 0 },
123
+ mode: { enum: ["loop", "burst"] },
124
+ start: num(0, 60),
125
+ velocity: point,
126
+ acceleration: point,
127
+ children: arr(ref("node"), 1),
128
+ }, ["count", "lifetime", "mode", "velocity", "children"]),
129
+ ],
130
+ },
131
+ gradient: {
132
+ oneOf: [
133
+ obj({ kind: { const: "linear" }, from: point, to: point, stops: arr(stop, 2, 32) }),
134
+ obj({ kind: { const: "radial" }, center: point, radius: expr, stops: arr(stop, 2, 32) }),
135
+ ],
136
+ },
137
+ },
138
+ };
139
+ /** Only the closed subset used above; this is not a general JSON Schema interpreter. */
140
+ export function checkStructure(value, rule = svgAssetSchema, path = "$", depth = 0) {
141
+ if (depth > 96)
142
+ return `${path}: nesting exceeds 96`;
143
+ if (rule.$ref)
144
+ return checkStructure(value, svgAssetSchema.$defs[rule.$ref.slice(8)], path, depth + 1);
145
+ if (rule.oneOf) {
146
+ const results = rule.oneOf.map((r) => checkStructure(value, r, path, depth + 1));
147
+ return results.filter((r) => r === null).length === 1 ? null : `${path}: does not match a supported form`;
148
+ }
149
+ if ("const" in rule && value !== rule.const)
150
+ return `${path}: expected ${String(rule.const)}`;
151
+ if (rule.enum && !rule.enum.includes(value))
152
+ return `${path}: unsupported value`;
153
+ if (rule.type === "number" || rule.type === "integer") {
154
+ if (typeof value !== "number" ||
155
+ !Number.isFinite(value) ||
156
+ (rule.type === "integer" && !Number.isInteger(value)))
157
+ return `${path}: expected finite ${rule.type}`;
158
+ if ((rule.minimum !== undefined && value < rule.minimum) ||
159
+ (rule.maximum !== undefined && value > rule.maximum) ||
160
+ (rule.exclusiveMinimum !== undefined && value <= rule.exclusiveMinimum))
161
+ return `${path}: number outside supported bounds`;
162
+ }
163
+ if (rule.type === "string") {
164
+ if (typeof value !== "string")
165
+ return `${path}: expected string`;
166
+ if ((rule.minLength && value.length < rule.minLength) ||
167
+ (rule.maxLength && value.length > rule.maxLength) ||
168
+ (rule.pattern && !new RegExp(rule.pattern).test(value)))
169
+ return `${path}: invalid string`;
170
+ }
171
+ if (rule.type === "boolean" && typeof value !== "boolean")
172
+ return `${path}: expected boolean`;
173
+ if (rule.type === "array") {
174
+ if (!Array.isArray(value))
175
+ return `${path}: expected array`;
176
+ if (value.length < (rule.minItems ?? 0) || value.length > (rule.maxItems ?? Infinity))
177
+ return `${path}: invalid array length`;
178
+ for (let i = 0; i < value.length; i++) {
179
+ const item = rule.prefixItems?.[i] ?? rule.items;
180
+ if (item === false)
181
+ return `${path}[${i}]: extra item`;
182
+ if (item) {
183
+ const error = checkStructure(value[i], item, `${path}[${i}]`, depth + 1);
184
+ if (error)
185
+ return error;
186
+ }
187
+ }
188
+ }
189
+ if (rule.type === "object") {
190
+ if (value === null || typeof value !== "object" || Array.isArray(value))
191
+ return `${path}: expected object`;
192
+ const record = value, keys = Object.keys(record);
193
+ if (keys.length > (rule.maxProperties ?? Infinity))
194
+ return `${path}: too many entries`;
195
+ for (const key of rule.required ?? [])
196
+ if (!Object.hasOwn(record, key))
197
+ return `${path}.${key}: required`;
198
+ for (const key of keys) {
199
+ if (rule.propertyNames) {
200
+ const error = checkStructure(key, rule.propertyNames, `${path}.${key}`, depth + 1);
201
+ if (error)
202
+ return error;
203
+ }
204
+ const child = rule.properties && Object.hasOwn(rule.properties, key)
205
+ ? rule.properties[key]
206
+ : rule.additionalProperties;
207
+ if (child === false)
208
+ return `${path}.${key}: unknown property`;
209
+ if (typeof child === "object") {
210
+ const error = checkStructure(record[key], child, `${path}.${key}`, depth + 1);
211
+ if (error)
212
+ return error;
213
+ }
214
+ }
215
+ }
216
+ return null;
217
+ }
@@ -0,0 +1,148 @@
1
+ /** SVG Asset Notation 0.1. Coordinates are SVG user units; time is seconds. */
2
+ export type SvgExpr = number | `$${string}` | [SvgOperator, ...SvgExpr[]];
3
+ export type SvgOperator = "add" | "sub" | "mul" | "div" | "min" | "max" | "sin" | "cos" | "abs" | "clamp" | "mix" | "rand";
4
+ export type SvgPoint = [SvgExpr, SvgExpr];
5
+ export type SvgPaint = string | {
6
+ gradient: string;
7
+ };
8
+ export interface SvgParameter {
9
+ default: number;
10
+ min: number;
11
+ max: number;
12
+ }
13
+ export interface SvgTransform {
14
+ translate?: SvgPoint;
15
+ rotate?: SvgExpr;
16
+ scale?: SvgPoint;
17
+ pivot?: SvgPoint;
18
+ }
19
+ export interface SvgStyle {
20
+ fill?: SvgPaint;
21
+ stroke?: SvgPaint;
22
+ strokeWidth?: SvgExpr;
23
+ linecap?: "butt" | "round" | "square";
24
+ linejoin?: "miter" | "round" | "bevel";
25
+ fillRule?: "nonzero" | "evenodd";
26
+ }
27
+ export interface SvgNodeBase {
28
+ id: string;
29
+ transform?: SvgTransform;
30
+ style?: SvgStyle;
31
+ opacity?: SvgExpr;
32
+ clip?: string;
33
+ mask?: string;
34
+ blur?: SvgExpr;
35
+ }
36
+ export type SvgPathCommand = ["M" | "L", SvgExpr, SvgExpr] | ["Q", SvgExpr, SvgExpr, SvgExpr, SvgExpr] | ["C", SvgExpr, SvgExpr, SvgExpr, SvgExpr, SvgExpr, SvgExpr] | ["A", SvgExpr, SvgExpr, SvgExpr, 0 | 1, 0 | 1, SvgExpr, SvgExpr] | ["Z"];
37
+ export type SvgShape = SvgNodeBase & ({
38
+ kind: "rect";
39
+ x: SvgExpr;
40
+ y: SvgExpr;
41
+ width: SvgExpr;
42
+ height: SvgExpr;
43
+ radius?: SvgExpr;
44
+ } | {
45
+ kind: "ellipse";
46
+ cx: SvgExpr;
47
+ cy: SvgExpr;
48
+ rx: SvgExpr;
49
+ ry: SvgExpr;
50
+ } | {
51
+ kind: "path";
52
+ d: SvgPathCommand[];
53
+ });
54
+ export type SvgNode = SvgShape | (SvgNodeBase & ({
55
+ kind: "group";
56
+ children: SvgNode[];
57
+ } | {
58
+ kind: "use";
59
+ symbol: string;
60
+ args?: Record<string, SvgExpr>;
61
+ } | {
62
+ kind: "repeat";
63
+ count: number;
64
+ children: SvgNode[];
65
+ } | {
66
+ kind: "emitter";
67
+ count: number;
68
+ lifetime: number;
69
+ mode: "loop" | "burst";
70
+ start?: number;
71
+ velocity: SvgPoint;
72
+ acceleration?: SvgPoint;
73
+ children: SvgNode[];
74
+ }));
75
+ export interface SvgSymbol {
76
+ params?: Record<string, SvgParameter>;
77
+ children: SvgNode[];
78
+ }
79
+ export type SvgGradient = {
80
+ stops: {
81
+ offset: number;
82
+ color: string;
83
+ opacity?: number;
84
+ }[];
85
+ } & ({
86
+ kind: "linear";
87
+ from: SvgPoint;
88
+ to: SvgPoint;
89
+ } | {
90
+ kind: "radial";
91
+ center: SvgPoint;
92
+ radius: SvgExpr;
93
+ });
94
+ export interface SvgKey {
95
+ time: number;
96
+ value: number;
97
+ ease?: "linear" | "smooth" | "hold";
98
+ }
99
+ export interface SvgClip {
100
+ duration: number;
101
+ loop: boolean;
102
+ tracks: Record<string, SvgKey[]>;
103
+ }
104
+ export interface SvgAssetSpec {
105
+ schema: "svg-asset/0.1";
106
+ name: string;
107
+ viewBox: [number, number, number, number];
108
+ seed?: number;
109
+ params?: Record<string, SvgParameter>;
110
+ palette?: Record<string, string>;
111
+ gradients?: Record<string, SvgGradient>;
112
+ clips?: Record<string, SvgShape[]>;
113
+ masks?: Record<string, SvgShape[]>;
114
+ symbols?: Record<string, SvgSymbol>;
115
+ animations?: Record<string, SvgClip>;
116
+ defaultAnimation?: string;
117
+ nodes: SvgNode[];
118
+ }
119
+ export interface SvgDiagnostic {
120
+ code: string;
121
+ path: string;
122
+ message: string;
123
+ }
124
+ export interface CompiledSvgAsset {
125
+ readonly spec: SvgAssetSpec;
126
+ }
127
+ export type SvgCompilation = {
128
+ ok: true;
129
+ asset: CompiledSvgAsset;
130
+ diagnostics: [];
131
+ } | {
132
+ ok: false;
133
+ asset: null;
134
+ diagnostics: SvgDiagnostic[];
135
+ };
136
+ export interface SvgFrameOptions {
137
+ /** Omit to select defaultAnimation; null renders the rest pose. */
138
+ animation?: string | null;
139
+ time?: number;
140
+ /** Overrides replace sampled animation values, within declared bounds. */
141
+ params?: Record<string, number>;
142
+ /** Set a different prefix for each SVG embedded in one HTML document. */
143
+ prefix?: string;
144
+ }
145
+ export interface SvgAnimationOptions extends Omit<SvgFrameOptions, "time" | "animation"> {
146
+ animation?: string;
147
+ fps?: number;
148
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/svg.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { CompiledSvgAsset, SvgAnimationOptions, SvgCompilation, SvgDiagnostic, SvgFrameOptions } from "./svg/types.js";
2
+ export type * from "./svg/types.js";
3
+ export declare const SVG_ASSET_LIMITS: Readonly<{
4
+ sourceValues: 100000;
5
+ sourceCharacters: 2000000;
6
+ nodesPerFrame: 10000;
7
+ frames: 240;
8
+ outputCharacters: 8000000;
9
+ depth: 32;
10
+ }>;
11
+ export declare class SvgAssetError extends Error {
12
+ readonly diagnostics: SvgDiagnostic[];
13
+ constructor(code: string, path: string, message: string);
14
+ }
15
+ /** Validates structure, references and the default/rest frame; never mutates caller input. */
16
+ export declare function compileSvgAsset(input: unknown): SvgCompilation;
17
+ export declare const validateSvgAsset: (input: unknown) => SvgDiagnostic[];
18
+ /** A seek is independent of every previous sample. Loop endpoints wrap; one-shots hold. */
19
+ export declare function sampleSvgParameters(asset: CompiledSvgAsset, options?: SvgFrameOptions): {
20
+ time: number;
21
+ phase: number;
22
+ params: Record<string, number>;
23
+ };
24
+ export declare function renderSvgFrame(asset: CompiledSvgAsset, options?: SvgFrameOptions): string;
25
+ /** Script-free sampled SVG playback. Frame sampling is deliberate, bounded and portable. */
26
+ export declare function exportSvgAnimation(asset: CompiledSvgAsset, options?: SvgAnimationOptions): string;