wobnimo 0.0.0-stage → 0.1.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.
@@ -0,0 +1,197 @@
1
+ import { E as Expression, B as BoneSpec, b as Channel, a as Axis, g as Motion, V as Vec3, C as CharacterSpec, H as Hex } from './types-PAZ6PnKv.js';
2
+
3
+ declare const STATES: readonly ["idle", "thinking", "listening", "speaking", "sleeping", "working", "error", "surprised", "celebrating", "sad"];
4
+ type MascotState = (typeof STATES)[number];
5
+ /** Every expression a face can show: the states plus the reaction to a poke. */
6
+ declare const EXPRESSIONS: readonly Expression[];
7
+
8
+ interface ResolvedBone extends BoneSpec {
9
+ index: number;
10
+ parentIndex: number;
11
+ }
12
+ declare class SpecError extends Error {
13
+ constructor(character: string, message: string);
14
+ }
15
+
16
+ type Mat4 = Float32Array;
17
+
18
+ type Channels = Record<Channel, number> & {
19
+ /** Scales idle waves: low when asleep, high when excited. */
20
+ energy: number;
21
+ };
22
+ /** Per bone: per expression, per axis, a motion. */
23
+ type BoneActing = Array<Array<[Expression, Axis, Motion]>>;
24
+ declare class Rig {
25
+ private readonly bones;
26
+ private readonly acting;
27
+ readonly world: Mat4[];
28
+ /** World → bone, packed for upload: 16 floats per bone. */
29
+ readonly inverse: Float32Array;
30
+ private readonly tips;
31
+ /** Spin accumulated by acting, per bone and axis. */
32
+ private readonly spun;
33
+ private readonly scratch;
34
+ constructor(bones: ResolvedBone[], acting?: BoneActing);
35
+ /** A point in a bone's space, in world space, as of the last update. */
36
+ point(bone: number, at: Vec3): Vec3;
37
+ /**
38
+ * Poses the skeleton. `root` places the whole character, `weights` blends
39
+ * each expression's acting, `dt` steps the jiggle springs and spins.
40
+ */
41
+ update(root: Mat4, ch: Channels, weights: Record<Expression, number>, time: number, dt: number): void;
42
+ private jiggle;
43
+ }
44
+
45
+ interface CompiledCharacter {
46
+ spec: CharacterSpec;
47
+ bones: ResolvedBone[];
48
+ acting: BoneActing;
49
+ anchors: Record<string, {
50
+ bone: number;
51
+ at: Vec3;
52
+ }>;
53
+ fragment: string;
54
+ /** Material names, in the order of their colours in `albedo`. */
55
+ materials: string[];
56
+ /** The spec's material colours, linear RGB, three floats each. */
57
+ albedo: Float32Array;
58
+ stats: {
59
+ parts: number;
60
+ bones: number;
61
+ materials: number;
62
+ shaderLines: number;
63
+ };
64
+ }
65
+ declare function compile(spec: CharacterSpec): CompiledCharacter;
66
+
67
+ /** New colours for materials, by name. Unknown names are an error, so typos don't fail silently. */
68
+ type Palette = Record<string, Hex>;
69
+ /** A copy of the spec with new material colours (and their shades). */
70
+ declare function recolor(spec: CharacterSpec, palette: Palette): CharacterSpec;
71
+ /**
72
+ * The compiled character's colours with a palette applied, ready for the
73
+ * renderer. Colours are uniforms, so this is how a live mascot recolours
74
+ * without building a new shader.
75
+ */
76
+ declare function albedo(character: CompiledCharacter, palette: Palette | undefined): Float32Array;
77
+ /**
78
+ * A new character from an existing one: a different id and name, colours,
79
+ * temperament, or anything else at the top level of the spec.
80
+ */
81
+ declare function variant(spec: CharacterSpec, changes: Partial<Omit<CharacterSpec, 'materials'>> & {
82
+ palette?: Palette;
83
+ }): CharacterSpec;
84
+ /** Identity, typed: write a spec with full autocompletion and checking. */
85
+ declare const defineCharacter: (spec: CharacterSpec) => CharacterSpec;
86
+
87
+ interface Animated {
88
+ /** Whether it is worth drawing this frame (on screen, not settled). */
89
+ readonly active: boolean;
90
+ tick(dt: number, time: number, quality: number): void;
91
+ }
92
+
93
+ interface MascotOptions {
94
+ /** Where it starts. Default `idle`. */
95
+ state?: MascotState;
96
+ /** 0–1. Desyncs blinks, glances and hops between mascots on one page. Random by default. */
97
+ seed?: number;
98
+ /** Follow the pointer and hop when clicked. Default true. */
99
+ interactive?: boolean;
100
+ /** Effects around the character (z's, sparkles, sound waves…). Default true. */
101
+ effects?: boolean;
102
+ /** Multiplies the pace of everything. Default 1. */
103
+ speed?: number;
104
+ /** Recolour materials by name, e.g. `{ shell: '#e0457b' }`. */
105
+ palette?: Palette;
106
+ /** Cap on device pixels per CSS pixel. Default 2. */
107
+ maxPixelRatio?: number;
108
+ /** The floor shadow's colour, linear RGB. */
109
+ shadow?: Vec3;
110
+ /** Strength of the floor shadow, 0–1. 0 suits a mascot laid over text or a busy background. Default 1. */
111
+ floor?: number;
112
+ /** Overall brightness. Default 1. */
113
+ exposure?: number;
114
+ /** Room around the character: 1 fits it snugly, larger values make it smaller in its canvas. Default 1. */
115
+ framing?: number;
116
+ /** Respect `prefers-reduced-motion`: hold the state's pose instead of animating. Default true. */
117
+ respectReducedMotion?: boolean;
118
+ }
119
+ interface MascotEvents {
120
+ /** Clicked or poked. */
121
+ poke: void;
122
+ state: MascotState;
123
+ /** The first frame is on screen: its shader is built. Fade it in from here. */
124
+ ready: void;
125
+ }
126
+ type Listener<T> = (value: T) => void;
127
+ declare function compileCached(spec: CharacterSpec): CompiledCharacter;
128
+ declare class Mascot implements Animated {
129
+ readonly canvas: HTMLCanvasElement;
130
+ private character;
131
+ private brain;
132
+ private rig;
133
+ private fx;
134
+ private readonly renderer;
135
+ private readonly options;
136
+ private readonly seed;
137
+ private palette;
138
+ private albedo;
139
+ private visible;
140
+ private paused;
141
+ private time;
142
+ private readonly reducedMotion;
143
+ private stillFrames;
144
+ private drawn;
145
+ private readonly listeners;
146
+ private readonly cleanup;
147
+ private source;
148
+ constructor(canvas: HTMLCanvasElement, spec: CharacterSpec, options?: MascotOptions);
149
+ get state(): MascotState;
150
+ /** The spec as given; a palette doesn't change it. */
151
+ get spec(): CharacterSpec;
152
+ get stats(): {
153
+ parts: number;
154
+ bones: number;
155
+ materials: number;
156
+ shaderLines: number;
157
+ };
158
+ setState(state: MascotState): void;
159
+ /** Swaps the character, keeping the state. Material names differ between characters, so the palette resets unless you pass one. */
160
+ setSpec(spec: CharacterSpec, palette?: Palette): void;
161
+ /** Recolours materials by name; `undefined` goes back to the spec's own colours. Cheap enough for a colour picker. */
162
+ setPalette(palette: Palette | undefined): void;
163
+ setSpeed(speed: number): void;
164
+ setEffects(on: boolean): void;
165
+ /** The happy reaction: a hop with a spin. */
166
+ poke(): void;
167
+ pause(): void;
168
+ play(): void;
169
+ /** Subscribes to an event; returns the unsubscribe function. */
170
+ on<K extends keyof MascotEvents>(event: K, listener: Listener<MascotEvents[K]>): () => void;
171
+ get active(): boolean;
172
+ tick(dt: number, _time: number, quality: number): void;
173
+ /** Whether the first frame has been drawn. */
174
+ get ready(): boolean;
175
+ dispose(): void;
176
+ private load;
177
+ private get still();
178
+ private emit;
179
+ private label;
180
+ private listen;
181
+ }
182
+ /**
183
+ * The shortest way in: creates a canvas that fills `container` and puts a
184
+ * mascot on it. `dispose()` removes the canvas again.
185
+ */
186
+ declare function mount(container: HTMLElement, spec: CharacterSpec, options?: MascotOptions): Mascot;
187
+
188
+ /** The chat statuses the AI SDK emits, and anything close enough to map. */
189
+ type ChatStatus = 'submitted' | 'streaming' | 'ready' | 'error' | 'idle';
190
+ /**
191
+ * Drives a mascot from a chat `status`. Call it whenever the status changes
192
+ * (in React, inside an effect on `status`). When a turn finishes (`ready`)
193
+ * the mascot celebrates for `celebrateMs`, then settles to idle.
194
+ */
195
+ declare function driveFromStatus(mascot: Mascot, status: ChatStatus, celebrateMs?: number): void;
196
+
197
+ export { type ChatStatus as C, EXPRESSIONS as E, type MascotOptions as M, type Palette as P, Rig as R, STATES as S, type MascotState as a, type Mat4 as b, type Channels as c, type CompiledCharacter as d, Mascot as e, type MascotEvents as f, SpecError as g, albedo as h, compile as i, compileCached as j, defineCharacter as k, driveFromStatus as l, mount as m, recolor as r, variant as v };
@@ -0,0 +1,308 @@
1
+ type Vec2 = [number, number];
2
+ type Vec3 = [number, number, number];
3
+ /** A colour as `#rrggbb`, in sRGB. */
4
+ type Hex = string;
5
+ /** The channels the behaviour layer drives every frame. Bones map them onto rotations. */
6
+ type Channel = 'yaw' | 'pitch' | 'roll' | 'flap' | 'talk';
7
+ type Axis = 'rx' | 'ry' | 'rz';
8
+ interface CharacterSpec {
9
+ id: string;
10
+ name: string;
11
+ /** One line on what makes this body plan different, for humans and agents. */
12
+ tagline: string;
13
+ /** The UI accent that goes with the character. */
14
+ accent: Hex;
15
+ /** How the camera frames the character: the height it looks at and the radius it must fit. */
16
+ frame: {
17
+ target: number;
18
+ radius: number;
19
+ };
20
+ /** The volume every ray is clipped to, as a sphere above the root: centre height and radius. */
21
+ bound: {
22
+ y: number;
23
+ radius: number;
24
+ };
25
+ motion?: Partial<Temperament>;
26
+ materials: Record<string, MaterialSpec>;
27
+ bones: BoneSpec[];
28
+ parts: PartSpec[];
29
+ paints?: PaintSpec[];
30
+ eyes?: EyesSpec;
31
+ face?: FaceSpec;
32
+ /** Named points on the body that effects come from: `top`, `mouth`, `ear`, `feet`, or your own. */
33
+ anchors?: Record<string, AnchorSpec>;
34
+ /** How each state moves each bone, on top of the rig. This is where a character acts. */
35
+ acting?: Partial<Record<Expression, Record<string, Partial<Record<Axis, Motion>>>>>;
36
+ /** Effects around the character, added to (or, with `replace`, instead of) the shared defaults. */
37
+ fx?: FxSpec;
38
+ /** The state's light spills onto the floor in a soft pool. */
39
+ glowPool?: number;
40
+ }
41
+ /** Every state a character can be in, plus the short happy reaction to a poke. */
42
+ type Expression = 'idle' | 'thinking' | 'listening' | 'speaking' | 'sleeping' | 'working' | 'error' | 'surprised' | 'celebrating' | 'sad' | 'happy';
43
+ /**
44
+ * A rotation over time: `base + amp · sin(2π · hz · t + phase)`, plus `spin`
45
+ * radians per second accumulated for as long as the state lasts.
46
+ * A bare number is a held angle.
47
+ */
48
+ type Motion = number | [base: number, amp?: number, hz?: number, phase?: number, spin?: number];
49
+ interface AnchorSpec {
50
+ bone: string;
51
+ at: Vec3;
52
+ }
53
+ /** The shapes an effect particle can take. */
54
+ type Glyph = 'z' | 'bubble' | 'sparkle' | 'heart' | 'note' | 'arc' | 'puff' | 'ring' | 'drop' | 'spark' | 'mote' | 'feather' | 'dot' | 'exclaim' | 'warning' | 'confetti' | 'cloud' | 'star' | 'gear';
55
+ /** When an emitter fires: continuously in a state, always, or on a beat of the animation. */
56
+ type FxTrigger = Expression | 'ambient' | 'land' | 'syllable' | `enter:${Expression}`;
57
+ interface EmitterSpec {
58
+ glyph: Glyph;
59
+ anchor: string;
60
+ /** Particles per second while active, or how many at once for an event. */
61
+ rate: number;
62
+ /** Where particles start, relative to the anchor, and how far apart. */
63
+ offset?: Vec3;
64
+ spread?: number;
65
+ /** Starting velocity, units per second, with random variation. */
66
+ velocity: Vec3;
67
+ jitter?: number;
68
+ /** Mirror half the particles across x (sound from both sides of a mouth). */
69
+ mirror?: boolean;
70
+ gravity?: number;
71
+ drag?: number;
72
+ /** Side-to-side drift, like a bubble or a falling feather. */
73
+ wobble?: number;
74
+ life: [number, number];
75
+ /** Size at birth and at death. */
76
+ size: [number, number];
77
+ /** Starting angle and spin, radians and radians per second. */
78
+ rotation?: number;
79
+ spin?: number;
80
+ /** Point along the direction of travel: sparks streak, drops trail their tips. */
81
+ align?: boolean;
82
+ color: Hex;
83
+ /** Adds light instead of covering what is behind it. */
84
+ glow?: boolean;
85
+ }
86
+ type FxSpec = Partial<Record<FxTrigger, EmitterSpec[]>> & {
87
+ replace?: FxTrigger[];
88
+ };
89
+ /** How a character moves, independent of what it looks like. */
90
+ interface Temperament {
91
+ /** Resting height above the floor; above zero the character floats. */
92
+ float: number;
93
+ /** How high a hop goes. */
94
+ hop: number;
95
+ /** Multiplies squash and stretch. */
96
+ squash: number;
97
+ /** Multiplies every spring's frequency: higher is snappier. */
98
+ snap: number;
99
+ /** How far the head turns toward things. */
100
+ look: number;
101
+ /** How the head moves between glances: 0 eases like a person, 1 snaps and holds like a bird. */
102
+ snapLook: number;
103
+ /** How much the body squashes on each syllable. */
104
+ talkSquash: number;
105
+ }
106
+ interface MaterialSpec {
107
+ color: Hex;
108
+ /**
109
+ * This material is a shade of another, by name: recolouring that one
110
+ * recolours this one too, keeping the same relation (a wing a little
111
+ * darker than the feathers, the body's fur the same as the head's).
112
+ */
113
+ shadeOf?: string;
114
+ /** Clear-coat reflectance face on; it rises to 1 at grazing angles. */
115
+ f0?: number;
116
+ /** Strength and tightness of the key light's highlight. */
117
+ spec?: number;
118
+ shininess?: number;
119
+ /** How much of the reflection takes the albedo's colour (0 plastic, 1 metal). */
120
+ metal?: number;
121
+ /** The cool back light rimming the silhouette. */
122
+ rim?: number;
123
+ /** How sharp reflections are, 0 (satin) to 1 (wet glass). */
124
+ gloss?: number;
125
+ /** Velvet sheen at grazing angles, for feathers and fuzz. */
126
+ sheen?: number;
127
+ /** Light scattered under the surface, for jelly, wax and ghosts. */
128
+ sss?: number;
129
+ sssColor?: Hex;
130
+ /** Light the material gives off on its own. */
131
+ emit?: Hex;
132
+ emitStrength?: number;
133
+ /** Takes its light from the state's glow colour: antenna bulbs, chest lamps. */
134
+ glows?: boolean;
135
+ /** Fine detail on top of the shape: fur, scales, freckles, a wet ripple. */
136
+ surface?: SurfaceSpec;
137
+ }
138
+ /**
139
+ * Procedural surface detail, fixed to a bone so it moves with the body.
140
+ * `fur`: fine fibres and soft clumps. `scales`: a pebbled hide with darker
141
+ * seams. `speckle`: scattered spots. `ripple`: a gentle unevenness that
142
+ * makes a glossy coat read as wet.
143
+ */
144
+ interface SurfaceSpec {
145
+ kind: 'fur' | 'scales' | 'speckle' | 'ripple';
146
+ /** Features per unit; a mascot is about one unit tall. */
147
+ scale: number;
148
+ /** 0–1. */
149
+ strength?: number;
150
+ /** Seams for scales, spots for speckle. */
151
+ color?: Hex;
152
+ /** The bone the pattern is fixed to. Default the root. */
153
+ bone?: string;
154
+ }
155
+ interface BoneSpec {
156
+ name: string;
157
+ /** Defaults to the root, which the behaviour layer moves (hops, floats, squash). */
158
+ parent?: string;
159
+ /** Where the bone pivots, in its parent's space. */
160
+ pivot: Vec3;
161
+ /** Rotation at rest, applied before everything else. */
162
+ rest?: Partial<Record<Axis, number>>;
163
+ /** Channels added to the rotation: `{ ry: [['yaw', 0.8]] }`. */
164
+ drive?: Partial<Record<Axis, Array<[Channel, number]>>>;
165
+ /** Idle oscillation, scaled by the state's energy: amplitude, Hz, phase. */
166
+ wave?: Partial<Record<Axis, [number, number, number]>>;
167
+ /** Secondary motion: the bone's tip is a mass on a spring that lags behind its parent. */
168
+ jiggle?: JiggleSpec;
169
+ }
170
+ interface JiggleSpec {
171
+ /** The direction the bone points at rest, in its own space. */
172
+ axis: Vec3;
173
+ length: number;
174
+ /** Spring frequency in Hz and damping ratio. */
175
+ freq: number;
176
+ damping: number;
177
+ /** The furthest the bone may swing from rest. */
178
+ maxAngle?: number;
179
+ }
180
+ type Op = {
181
+ op?: 'add';
182
+ blend?: number;
183
+ } | {
184
+ op: 'sub';
185
+ blend?: number;
186
+ };
187
+ interface PartBase {
188
+ bone: string;
189
+ material: string;
190
+ /** Offset and rotation inside the bone's space. */
191
+ at?: Vec3;
192
+ rot?: Vec3;
193
+ /**
194
+ * Keeps only the side of a plane that `normal` points away from, in the
195
+ * part's own space: `{ normal: [0, -1, 0] }` keeps the top half. Halves a
196
+ * beak, flattens a base.
197
+ */
198
+ clip?: {
199
+ normal: Vec3;
200
+ offset?: number;
201
+ };
202
+ }
203
+ type PartSpec = PartBase & Op & ({
204
+ shape: 'sphere';
205
+ radius: number;
206
+ } | {
207
+ shape: 'ellipsoid';
208
+ radii: Vec3;
209
+ } | {
210
+ shape: 'roundBox';
211
+ half: Vec3;
212
+ round: number;
213
+ } | {
214
+ shape: 'capsule';
215
+ from: Vec3;
216
+ to: Vec3;
217
+ radius: number;
218
+ } | {
219
+ shape: 'roundCone';
220
+ from: Vec3;
221
+ to: Vec3;
222
+ r1: number;
223
+ r2: number;
224
+ } | {
225
+ shape: 'cylinder';
226
+ radius: number;
227
+ half: number;
228
+ round: number;
229
+ } | {
230
+ shape: 'frame';
231
+ half: Vec2;
232
+ corner: number;
233
+ width: number;
234
+ depth: number;
235
+ }
236
+ /** A rounded rectangle extruded along z: screens, panels, plates. */
237
+ | {
238
+ shape: 'plate';
239
+ half: Vec2;
240
+ corner: number;
241
+ depth: number;
242
+ }
243
+ /** Cuts everything so far off below a wavy line: a ghost's hem. */
244
+ | {
245
+ shape: 'hem';
246
+ y: number;
247
+ amplitude: number;
248
+ waves: number;
249
+ speed: number;
250
+ blend: number;
251
+ });
252
+ /** Colour painted onto a surface in a bone's space: bellies, blush, spots. */
253
+ interface PaintSpec {
254
+ bone: string;
255
+ on: string[];
256
+ color: Hex;
257
+ /** Softness of the edge, as a share of the size. */
258
+ soft?: number;
259
+ opacity?: number;
260
+ /** `sphere`: everything within `radius` of `at`. `ellipse`: projected along +z. */
261
+ kind: 'sphere' | 'ellipse';
262
+ at: Vec3;
263
+ radius?: number;
264
+ radii?: Vec2;
265
+ }
266
+ type EyesSpec = {
267
+ kind: 'ball';
268
+ bone: string;
269
+ at: [Vec3, Vec3];
270
+ radius: number;
271
+ iris: Hex;
272
+ /** Angular size of the iris and pupil, as the sine of their half-angle. */
273
+ irisSize: number;
274
+ pupilSize: number;
275
+ /** Pupil width over height; above 1 for a goat or an octopus. */
276
+ pupilAspect?: number;
277
+ /** What the lids are made of, usually the skin. */
278
+ lid: string;
279
+ } | {
280
+ kind: 'bead';
281
+ bone: string;
282
+ at: [Vec3, Vec3];
283
+ radii: Vec3;
284
+ material: string;
285
+ };
286
+ type FaceSpec = {
287
+ /** An LED matrix on a flat screen, facing +z. */
288
+ kind: 'screen';
289
+ bone: string;
290
+ material: string;
291
+ center: Vec2;
292
+ half: Vec2;
293
+ corner: number;
294
+ /** The pitch of the LED grid. */
295
+ cell: number;
296
+ color: Hex;
297
+ } | {
298
+ /** A painted mouth that smiles and talks. */
299
+ kind: 'mouth';
300
+ bone: string;
301
+ on: string[];
302
+ at: Vec3;
303
+ width: number;
304
+ color: Hex;
305
+ tongue: Hex;
306
+ };
307
+
308
+ export type { AnchorSpec as A, BoneSpec as B, CharacterSpec as C, Expression as E, FxSpec as F, Glyph as G, Hex as H, JiggleSpec as J, MaterialSpec as M, Op as O, PaintSpec as P, SurfaceSpec as S, Temperament as T, Vec3 as V, Axis as a, Channel as b, EmitterSpec as c, EyesSpec as d, FaceSpec as e, FxTrigger as f, Motion as g, PartSpec as h, Vec2 as i };