@glissade/scene 0.18.0-pre.5 → 0.18.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/describe.d.ts +25 -3
- package/dist/describe.js +139 -7
- package/package.json +2 -2
package/dist/describe.d.ts
CHANGED
|
@@ -20,21 +20,43 @@
|
|
|
20
20
|
* never pulled onto the base embed path — a scene that never calls `describe()`
|
|
21
21
|
* pays zero bytes for it.
|
|
22
22
|
*/
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* One prop in the manifest. The `animatable` flag is the load-bearing
|
|
25
|
+
* distinction an AI reads to tell "animate this" from "set at construction":
|
|
26
|
+
*
|
|
27
|
+
* - An ANIMATABLE prop (`animatable: true`) carries a `target` — a real track
|
|
28
|
+
* target you bind via `to`/`fromTo`/`set`. Generated from `listTargets()`,
|
|
29
|
+
* so it is a registered target by construction.
|
|
30
|
+
* - A CONSTRUCTION prop (`animatable: false`) has NO `target` — it is passed to
|
|
31
|
+
* the node constructor and NEVER bindable (binding it is rejected by the bind
|
|
32
|
+
* guard). `required: true` marks one you can't omit (e.g. Image `assetId`).
|
|
33
|
+
*
|
|
34
|
+
* This negative space is the manifest's point: it prevents an AI from
|
|
35
|
+
* attempting to animate a construction-only prop (an `assetId` track, a
|
|
36
|
+
* `fontFamily` tween) — those are construction-time decisions, not tracks.
|
|
37
|
+
*/
|
|
24
38
|
interface DescribedProp {
|
|
25
39
|
/** The §2.2 value-type id this prop accepts (e.g. `'vec2'`, `'number'`, `'color'`). */
|
|
26
40
|
type: string;
|
|
27
|
-
/** Whether a Track can drive it
|
|
41
|
+
/** Whether a Track can drive it. `true` ⇒ a `target` is present; `false` ⇒ construction-only, no `target`. */
|
|
28
42
|
animatable: boolean;
|
|
29
|
-
/** The track-target template, `'<id>/<path>'` — substitute the node's real id. */
|
|
43
|
+
/** The track-target template, `'<id>/<path>'` — present ONLY on animatable props; substitute the node's real id. */
|
|
30
44
|
target?: string;
|
|
31
45
|
/** Component count of the value (`vec2` → 2, scalar → 1); omitted for non-numeric reprs. */
|
|
32
46
|
arity?: number;
|
|
47
|
+
/** Construction-only props: `true` when the constructor REQUIRES it (e.g. Image/Video `assetId`). */
|
|
48
|
+
required?: boolean;
|
|
33
49
|
}
|
|
34
50
|
interface DescribedNode {
|
|
35
51
|
props: {
|
|
36
52
|
[prop: string]: DescribedProp;
|
|
37
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* The tree-shakeable subpath this node is imported from, when not the base
|
|
56
|
+
* `@glissade/scene` index (e.g. the Layout family lives on
|
|
57
|
+
* `@glissade/scene/layout`). Omitted for base-index nodes.
|
|
58
|
+
*/
|
|
59
|
+
subpath?: string;
|
|
38
60
|
}
|
|
39
61
|
interface DescribedBuilderMethod {
|
|
40
62
|
name: string;
|
package/dist/describe.js
CHANGED
|
@@ -22,7 +22,7 @@ import { easings, listValueTypes } from "@glissade/core";
|
|
|
22
22
|
* never pulled onto the base embed path — a scene that never calls `describe()`
|
|
23
23
|
* pays zero bytes for it.
|
|
24
24
|
*/
|
|
25
|
-
const RAW_VERSION = "0.18.0
|
|
25
|
+
const RAW_VERSION = "0.18.0";
|
|
26
26
|
const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
|
|
27
27
|
/** Arity of a value type's numeric repr: vec2/vec2-arc → 2, number → 1; others (color/paint/path/string/boolean) carry no scalar arity. */
|
|
28
28
|
function arityOf(type) {
|
|
@@ -39,9 +39,71 @@ function expectsToType(expects) {
|
|
|
39
39
|
if (expects === void 0) return "unknown";
|
|
40
40
|
return Array.isArray(expects) ? expects.join("|") : expects;
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Construction-only props by node type — the difference between a node's Props
|
|
44
|
+
* interface and its `registerTarget` set (its animatable props). These are set
|
|
45
|
+
* once at `new Node({...})` and can never be animated.
|
|
46
|
+
*
|
|
47
|
+
* Base `NodeProps` construction props (`id`, `blend`, `filters`, `anchor`,
|
|
48
|
+
* `cache`) are shared by EVERY node and merged in separately, so this map holds
|
|
49
|
+
* only each node's OWN construction surface.
|
|
50
|
+
*/
|
|
51
|
+
const CONSTRUCTION_PROPS = {
|
|
52
|
+
Group: { children: { type: "Node[]" } },
|
|
53
|
+
Rect: {
|
|
54
|
+
sketch: { type: "SketchStyle" },
|
|
55
|
+
sketchFill: { type: "HachureSpec" },
|
|
56
|
+
sketchSeed: { type: "number" }
|
|
57
|
+
},
|
|
58
|
+
Circle: {
|
|
59
|
+
sketch: { type: "SketchStyle" },
|
|
60
|
+
sketchFill: { type: "HachureSpec" },
|
|
61
|
+
sketchSeed: { type: "number" }
|
|
62
|
+
},
|
|
63
|
+
Path: {
|
|
64
|
+
sketch: { type: "SketchStyle" },
|
|
65
|
+
sketchFill: { type: "HachureSpec" },
|
|
66
|
+
sketchSeed: { type: "number" }
|
|
67
|
+
},
|
|
68
|
+
Text: {
|
|
69
|
+
fontFamily: { type: "string" },
|
|
70
|
+
fontWeight: { type: "number" },
|
|
71
|
+
fontStyle: { type: "'normal'|'italic'" },
|
|
72
|
+
align: { type: "'left'|'center'|'right'" },
|
|
73
|
+
lineHeight: { type: "number" }
|
|
74
|
+
},
|
|
75
|
+
Image: { assetId: {
|
|
76
|
+
type: "string",
|
|
77
|
+
required: true
|
|
78
|
+
} },
|
|
79
|
+
Video: {
|
|
80
|
+
assetId: {
|
|
81
|
+
type: "string",
|
|
82
|
+
required: true
|
|
83
|
+
},
|
|
84
|
+
at: { type: "number" },
|
|
85
|
+
trimStart: { type: "number" },
|
|
86
|
+
playbackRate: { type: "number" },
|
|
87
|
+
clipDuration: { type: "number" },
|
|
88
|
+
sourceFps: { type: "number" }
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Base-`NodeProps` construction props shared by every node — set at
|
|
93
|
+
* construction, never animatable (none is a registered target).
|
|
94
|
+
*/
|
|
95
|
+
const BASE_CONSTRUCTION_PROPS = {
|
|
96
|
+
id: { type: "string" },
|
|
97
|
+
blend: { type: "BlendMode" },
|
|
98
|
+
filters: { type: "FilterSpec[]" },
|
|
99
|
+
anchor: { type: "AnchorSpec" },
|
|
100
|
+
cache: { type: "boolean" }
|
|
101
|
+
};
|
|
102
|
+
/** Introspect one freshly-instantiated node into its prop manifest: the
|
|
103
|
+
* ANIMATABLE props come from the REAL `registerTarget` calls via `listTargets()`
|
|
104
|
+
* (so they can't drift); the CONSTRUCTION-only props are merged from the curated
|
|
105
|
+
* schema (drift-guarded by a constructor test). */
|
|
106
|
+
function describeNode(node, typeName) {
|
|
45
107
|
const props = {};
|
|
46
108
|
for (const { path, expects } of node.listTargets()) {
|
|
47
109
|
const type = expectsToType(expects);
|
|
@@ -53,8 +115,71 @@ function describeNode(node) {
|
|
|
53
115
|
...arity !== void 0 ? { arity } : {}
|
|
54
116
|
};
|
|
55
117
|
}
|
|
118
|
+
const construction = {
|
|
119
|
+
...BASE_CONSTRUCTION_PROPS,
|
|
120
|
+
...CONSTRUCTION_PROPS[typeName] ?? {}
|
|
121
|
+
};
|
|
122
|
+
for (const [prop, spec] of Object.entries(construction)) props[prop] = {
|
|
123
|
+
type: spec.type,
|
|
124
|
+
animatable: false,
|
|
125
|
+
...spec.required ? { required: true } : {}
|
|
126
|
+
};
|
|
56
127
|
return { props };
|
|
57
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* The Layout family — `Layout` and its `Stack`/`Row`/`Column` ergonomic
|
|
131
|
+
* factories — lives on the budgeted `@glissade/scene/layout` entry (it ships
|
|
132
|
+
* Yoga wasm), so describe() does NOT import it (that would drag Yoga onto the
|
|
133
|
+
* describe/browser bundle). Instead it carries a CURATED schema, drift-guarded
|
|
134
|
+
* by `describe.test.ts`, which imports the real Layout and asserts these
|
|
135
|
+
* animatable props match its `listTargets()` and these construction props match
|
|
136
|
+
* its constructor.
|
|
137
|
+
*
|
|
138
|
+
* `width`/`height` are animatable number targets; `direction`/`justify`/`align`
|
|
139
|
+
* are construction-only (set once, not tweened). `children` is construction.
|
|
140
|
+
*/
|
|
141
|
+
const LAYOUT_SUBPATH = "@glissade/scene/layout";
|
|
142
|
+
const LAYOUT_ANIMATABLE = {
|
|
143
|
+
width: { type: "number" },
|
|
144
|
+
height: { type: "number" },
|
|
145
|
+
gap: { type: "number" },
|
|
146
|
+
padding: { type: "number" }
|
|
147
|
+
};
|
|
148
|
+
const LAYOUT_CONSTRUCTION = {
|
|
149
|
+
direction: { type: "'row'|'column'" },
|
|
150
|
+
justify: { type: "'start'|'center'|'end'|'space-between'|'space-around'" },
|
|
151
|
+
align: { type: "'start'|'center'|'end'|'stretch'" },
|
|
152
|
+
children: { type: "Node[]" }
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Build a Layout-family node manifest from the curated schema. Layout is a
|
|
156
|
+
* `Group` subclass, so it inherits every base transform target — reuse a
|
|
157
|
+
* throwaway `Group`'s `listTargets()` for those (no drift on the inherited set),
|
|
158
|
+
* then add the layout-specific animatable + construction props.
|
|
159
|
+
*/
|
|
160
|
+
function describeLayoutNode() {
|
|
161
|
+
const props = describeNode(new Group(), "Group").props;
|
|
162
|
+
delete props.children;
|
|
163
|
+
for (const [prop, spec] of Object.entries(LAYOUT_ANIMATABLE)) {
|
|
164
|
+
const arity = arityOf(spec.type);
|
|
165
|
+
props[prop] = {
|
|
166
|
+
type: spec.type,
|
|
167
|
+
animatable: true,
|
|
168
|
+
target: `<id>/${prop}`,
|
|
169
|
+
...arity !== void 0 ? { arity } : {}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const construction = LAYOUT_CONSTRUCTION;
|
|
173
|
+
for (const [prop, spec] of Object.entries(construction)) props[prop] = {
|
|
174
|
+
type: spec.type,
|
|
175
|
+
animatable: false,
|
|
176
|
+
...spec.required ? { required: true } : {}
|
|
177
|
+
};
|
|
178
|
+
return {
|
|
179
|
+
props,
|
|
180
|
+
subpath: LAYOUT_SUBPATH
|
|
181
|
+
};
|
|
182
|
+
}
|
|
58
183
|
const NODE_FACTORIES = {
|
|
59
184
|
Group: () => new Group(),
|
|
60
185
|
Rect: () => new Rect(),
|
|
@@ -80,7 +205,7 @@ const BUILDER_METHODS = [
|
|
|
80
205
|
},
|
|
81
206
|
{
|
|
82
207
|
name: "stagger",
|
|
83
|
-
signature: "stagger<T>(targets, { to, from?, duration?, ease? }, { each, anchor?, at? }): TimelineBuilder"
|
|
208
|
+
signature: "stagger<T>(targets, { to, from?, duration?, ease? }, { each: number | ((rank, count) => number), anchor?, at? }): TimelineBuilder"
|
|
84
209
|
},
|
|
85
210
|
{
|
|
86
211
|
name: "set",
|
|
@@ -138,14 +263,21 @@ const SUBPATHS = {
|
|
|
138
263
|
*/
|
|
139
264
|
function describe() {
|
|
140
265
|
const nodes = {};
|
|
141
|
-
for (const [name, factory] of Object.entries(NODE_FACTORIES)) nodes[name] = describeNode(factory());
|
|
266
|
+
for (const [name, factory] of Object.entries(NODE_FACTORIES)) nodes[name] = describeNode(factory(), name);
|
|
267
|
+
const layout = describeLayoutNode();
|
|
268
|
+
for (const name of [
|
|
269
|
+
"Layout",
|
|
270
|
+
"Stack",
|
|
271
|
+
"Row",
|
|
272
|
+
"Column"
|
|
273
|
+
]) nodes[name] = layout;
|
|
142
274
|
return {
|
|
143
275
|
version: PACKAGE_VERSION,
|
|
144
276
|
nodes,
|
|
145
277
|
valueTypes: listValueTypes(),
|
|
146
278
|
easings: Object.keys(easings),
|
|
147
279
|
builder: { methods: BUILDER_METHODS },
|
|
148
|
-
createScene: "createScene({ size: { w, h }, children: Node[] }): Scene",
|
|
280
|
+
createScene: "createScene({ size: { w, h }, children: Node[] }): Scene — media assets are declared on the Timeline document: timeline({ assets: { <id>: { kind: 'image'|'video', url } } }); an Image/Video node's `assetId` names an entry here.",
|
|
149
281
|
subpaths: SUBPATHS
|
|
150
282
|
};
|
|
151
283
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/scene",
|
|
3
|
-
"version": "0.18.0
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "glissade scene graph: nodes, transforms, DisplayList emission. Renderer-agnostic; zero DOM/Node dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"yoga-layout": "^3.2.1",
|
|
34
|
-
"@glissade/core": "0.18.0
|
|
34
|
+
"@glissade/core": "0.18.0"
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|