@semio/utils 0.0.0 → 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.
package/dist/index.d.mts CHANGED
@@ -1,13 +1,12 @@
1
1
  import * as react from 'react';
2
+ import { Context, ComponentType, ReactNode } from 'react';
2
3
  import * as zustand from 'zustand';
4
+ import { OrthographicCamera, PerspectiveCamera } from 'three';
3
5
 
4
6
  /**
5
7
  * Logs a message to the console.
6
8
  *
7
9
  * @param value - The message to be logged
8
- *
9
- * @remarks
10
- * This function temporarily disables ESLint console warnings for testing purposes.
11
10
  */
12
11
  declare function log(value: string): void;
13
12
 
@@ -56,38 +55,56 @@ declare function getId(lookup: string): string;
56
55
  * Time representation in milliseconds.
57
56
  */
58
57
  type RawTime = number;
58
+ declare enum PlayerDirection {
59
+ Reverse = "reverse",
60
+ Forward = "forward"
61
+ }
59
62
  /**
60
63
  * A timer implementation for managing animation playback.
61
64
  *
62
- * The timer operates on a normalized time range of [0, 1], where:
65
+ * The timer operates in milliseconds, where:
63
66
  * - 0 represents the start of the animation
64
- * - 1 represents the end of the animation
67
+ * - duration (in ms) represents the end of the animation
68
+ *
69
+ * Fields prefixed with `_` (e.g. `_previousTime`, `_currentDirection`,
70
+ * `_bounced`, `_enteredBounds`) are internal bookkeeping for the
71
+ * `updated()` tick function and should not be read by external
72
+ * consumers. They're on the public type because immutable spread-and-
73
+ * return pattern means every `withX` helper must roundtrip them.
65
74
  *
66
75
  * @property running - Whether the timer is actively running
67
- * @property _previousTime - The last update time in milliseconds
68
- * @property _currentTime - The current time in milliseconds
69
- * @property stamp - Current normalized time position [0, 1]
70
- * @property timescale - Playback speed multiplier (negative for reverse)
71
- * @property bounds - Constrains playback to a [start, end] range within [0, 1]
72
- * @property playback - Animation behavior at bounds ("loop"|"bounce"|"once")
73
- * @property viewport - Visible time range as [start, end] within [0, 1]
74
- * @property duration - Total animation duration in milliseconds
76
+ * @property _previousTime - INTERNAL. Last update time in ms.
77
+ * @property _currentTime - INTERNAL. Current update time in ms.
78
+ * @property stamp - Current time position in milliseconds
79
+ * @property speed - Playback speed multiplier (always positive)
80
+ * @property direction - Initial playback direction (PlayerDirection.Forward or PlayerDirection.Reverse)
81
+ * @property _currentDirection - INTERNAL. Actual movement direction during bouncing.
82
+ * @property _bounced - INTERNAL. Whether the player has already bounced this run.
83
+ * @property _enteredBounds - INTERNAL. Whether the player has crossed into bounds at least once.
84
+ * @property bounds - Constrains playback to a [start, end] range in milliseconds
85
+ * @property bounce - Whether to reverse direction when reaching bounds
86
+ * @property looping - Whether to continue playing after reaching bounds
87
+ * @property duration - Total animation duration in milliseconds (used for playback stepping)
75
88
  */
76
89
  interface Player {
77
90
  running: boolean;
78
91
  _previousTime: RawTime;
79
92
  _currentTime: RawTime;
80
93
  stamp: number;
81
- timescale: number;
94
+ speed: number;
95
+ direction: PlayerDirection;
96
+ _currentDirection: PlayerDirection;
97
+ _bounced: boolean;
98
+ _enteredBounds: boolean;
82
99
  bounds: [number, number];
83
- playback: "loop" | "bounce" | "once";
84
- viewport: [number, number];
100
+ bounce: boolean;
101
+ looping: boolean;
85
102
  duration: number;
86
103
  }
87
104
  /**
88
105
  * Creates a copy of the provided timer, but updated with the new timestamp.
89
106
  * @param timer - the timer to be updated
90
- * @param stamp - the new timestamp in the range [0, 1]
107
+ * @param stamp - the new timestamp in milliseconds
91
108
  * @returns the updated timer
92
109
  */
93
110
  declare function reset(player: Player, stamp?: number): Player;
@@ -102,38 +119,34 @@ declare function now(): RawTime;
102
119
  * @param timer - the timer to be updated
103
120
  * @returns the updated timer
104
121
  */
105
- declare function update(player: Player, coldStart: boolean): Player;
122
+ declare function updated(player: Player, coldStart: boolean): Player;
106
123
  /**
107
124
  * Sets the bounds for the player.
108
125
  * @param player - the player to be updated
109
- * @param bounds - the new bounds in the range [0, 1]
126
+ * @param bounds - the new bounds in milliseconds [start, end]
110
127
  * @returns the updated player
111
128
  */
112
- declare function setBounds(player: Player, bounds: [number, number]): Player;
129
+ declare function withBounds(player: Player, bounds: [number, number]): Player;
113
130
  /**
114
- * Sets the viewport for the player
115
- * @param player - the player to be updated
116
- * @param viewport - the new viewport in the range [0, 1]
117
- * @returns the updated player
118
- */
119
- declare function setViewport(player: Player, viewport: [number, number]): Player;
120
- /**
121
- * Creates a copy of the provided timer, but updated with a timescale based on the speed provided.
131
+ * Creates a copy of the provided timer, but updated with speed and direction.
122
132
  * @param timer - the timer to be updated
123
- * @param speed - the speed of playback desired (defaults to 1)
133
+ * @param speed - the speed of playback desired (defaults to current speed)
134
+ * @param direction - the direction of playback desired (defaults to current direction)
124
135
  * @returns the updated timer
125
136
  */
126
- declare function play(player: Player, speed?: number): Player;
137
+ declare function play(player: Player, speed?: number, direction?: PlayerDirection.Forward | PlayerDirection.Reverse): Player;
127
138
  /**
128
- * Creates a copy of the provided timer, but updated with a timescale of 0 (paused)
139
+ * Creates a copy of the provided timer, but paused
129
140
  * @param timer - the timer to be updated
130
141
  * @returns the updated timer
131
142
  */
132
143
  declare function pause(player: Player): Player;
133
144
  /**
134
- * Returns the provided player, with the viewport centered around the provided stamp as much as possible.
145
+ * Returns the provided player, reset to the given stamp.
146
+ * Pure stamp-set; viewport-follow is handled separately by the
147
+ * viewport-follow subscription.
135
148
  * @param player - the player to be updated
136
- * @param stamp - the new stamp to center the viewport around
149
+ * @param stamp - the new stamp
137
150
  * @returns the updated player
138
151
  */
139
152
  declare function seek(player: Player, stamp: number): Player;
@@ -143,21 +156,55 @@ declare function seek(player: Player, stamp: number): Player;
143
156
  * @param duration - the new duration
144
157
  * @returns the updated player
145
158
  */
146
- declare function setDuration(player: Player, duration: number): Player;
159
+ declare function withDuration(player: Player, duration: number): Player;
147
160
  /**
148
161
  * Creates a new Player instance with default values.
149
162
  *
150
163
  * @returns A new Player instance initialized with:
151
164
  * - Not running
152
165
  * - Current timestamp
153
- * - Zero stamp position
154
- * - No timescale
155
- * - Full bounds [0, 1]
156
- * - Loop playback
157
- * - Full viewport [0, 1]
158
- * - 1000ms duration
166
+ * - Zero stamp position (0ms)
167
+ * - Normal speed (1x)
168
+ * - Forward direction
169
+ * - Default bounds [0, 5000] ms
170
+ * - Looping enabled, bounce disabled
171
+ * - 5000ms duration
159
172
  */
160
173
  declare function newPlayer(): Player;
174
+ /**
175
+ * Sets the playback speed without affecting direction.
176
+ * @param player - the player to be updated
177
+ * @param speed - the new speed (must be positive)
178
+ * @returns the updated player
179
+ */
180
+ declare function withSpeed(player: Player, speed: number): Player;
181
+ /**
182
+ * Sets the playback direction without affecting speed.
183
+ * @param player - the player to be updated
184
+ * @param direction - the new direction
185
+ * @returns the updated player
186
+ */
187
+ declare function withDirection(player: Player, direction: PlayerDirection.Forward | PlayerDirection.Reverse): Player;
188
+ /**
189
+ * Reverses the current playback direction.
190
+ * @param player - the player to be updated
191
+ * @returns the updated player
192
+ */
193
+ declare function reversed(player: Player): Player;
194
+ /**
195
+ * Sets the bounce behavior for the player.
196
+ * @param player - the player to be updated
197
+ * @param bounce - whether to bounce at boundaries
198
+ * @returns the updated player
199
+ */
200
+ declare function withBounce(player: Player, bounce: boolean): Player;
201
+ /**
202
+ * Sets the looping behavior for the player.
203
+ * @param player - the player to be updated
204
+ * @param looping - whether to loop at boundaries
205
+ * @returns the updated player
206
+ */
207
+ declare function withLooping(player: Player, looping: boolean): Player;
161
208
 
162
209
  /**
163
210
  * Core data structure for player state
@@ -172,12 +219,16 @@ interface PlayerData {
172
219
  interface PlayerActions {
173
220
  /** Updates the total duration of the animation */
174
221
  updateDuration: (duration: number) => void;
175
- /** Updates the playback speed multiplier */
176
- updateTimescale: (speed: number) => void;
222
+ /** Updates the playback speed */
223
+ updateSpeed: (speed: number) => void;
224
+ /** Updates the playback direction */
225
+ updateDirection: (direction: PlayerDirection) => void;
226
+ /** Reverses the current playback direction */
227
+ reverseDirection: () => void;
177
228
  /** Resets the player to initial state or specified time */
178
229
  resetPlayer: (time?: number) => void;
179
230
  /** Plays the animation */
180
- playPlayer: (speed?: number) => void;
231
+ playPlayer: (speed?: number, direction?: PlayerDirection) => void;
181
232
  /** Pauses the animation */
182
233
  pausePlayer: () => void;
183
234
  /** Updates the timer */
@@ -186,28 +237,27 @@ interface PlayerActions {
186
237
  updatePlayerBounds: (start: number, end: number) => void;
187
238
  /** Updates one bound of the player */
188
239
  updatePlayerBound: (bound: "start" | "end", time?: number) => void;
189
- /** Sets the player viewport */
190
- updatePlayerViewport: (start: number, end: number) => void;
191
- /** Seeks the viewport to center the player at a time */
192
- updatePlayerViewportCenter: (time?: number) => void;
193
- /** Updates one bound of the player viewport */
194
- updatePlayerViewportBound: (bound: "start" | "end", time: number) => void;
240
+ /** Update the player bounce setting */
241
+ updateBounce: (bounce: boolean) => void;
242
+ /** Update the player looping setting */
243
+ updateLooping: (looping: boolean) => void;
195
244
  }
196
- type PlayerStoreSetter = (partial: (PlayerData & PlayerActions) | Partial<PlayerData & PlayerActions> | ((state: PlayerData & PlayerActions) => (PlayerData & PlayerActions) | Partial<PlayerData & PlayerActions>), replace?: false | undefined) => void;
245
+ type PlayerStoreSetter = (partial: (PlayerData & PlayerActions) | Partial<PlayerData & PlayerActions> | ((state: PlayerData & PlayerActions) => (PlayerData & PlayerActions) | Partial<PlayerData & PlayerActions>), replace?: false) => void;
197
246
  type PlayerStoreGetter = () => PlayerData & PlayerActions;
198
247
  declare const PlayerSlice: (set: PlayerStoreSetter) => {
199
248
  player: Player;
200
249
  updateDuration: (duration: number) => void;
201
- updateTimescale: (speed: number) => void;
250
+ updateSpeed: (speed: number) => void;
251
+ updateDirection: (direction: PlayerDirection) => void;
252
+ reverseDirection: () => void;
202
253
  resetPlayer: (time?: number) => void;
203
- playPlayer: (speed?: number) => void;
254
+ playPlayer: (speed?: number, direction?: PlayerDirection) => void;
204
255
  pausePlayer: () => void;
205
256
  updatePlayer: (coldStart?: boolean) => void;
206
257
  updatePlayerBounds: (start: number, end: number) => void;
207
258
  updatePlayerBound: (bound: "start" | "end", time?: number) => void;
208
- updatePlayerViewport: (start: number, end: number) => void;
209
- updatePlayerViewportCenter: (time?: number) => void;
210
- updatePlayerViewportBound: (bound: "start" | "end", time: number) => void;
259
+ updateBounce: (bounce: boolean) => void;
260
+ updateLooping: (looping: boolean) => void;
211
261
  };
212
262
  declare const usePlayerStore: zustand.UseBoundStore<Omit<zustand.StoreApi<PlayerData & PlayerActions>, "subscribe"> & {
213
263
  subscribe: {
@@ -242,9 +292,9 @@ interface RawVector2 {
242
292
  y: number;
243
293
  }
244
294
  interface RawEuler {
245
- x: number;
295
+ r: number;
296
+ p: number;
246
297
  y: number;
247
- z: number;
248
298
  }
249
299
  interface RawRGB {
250
300
  r: number;
@@ -264,7 +314,33 @@ interface AnimatablePubInfo {
264
314
  output: string;
265
315
  color?: string;
266
316
  }
267
- type AnimatableValue = AnimatableBoolean | AnimatableNumber | AnimatableString | AnimatableVector3 | AnimatableVector2 | AnimatableEuler | AnimatableColor;
317
+ declare enum AnimatableGroupType {
318
+ Vector2 = "vector2",
319
+ Vector3 = "vector3",
320
+ Euler = "euler",
321
+ RGB = "rgb",
322
+ HSL = "hsl",
323
+ Group = "group",
324
+ /** Entity-level group (spans all instances for one entity/namespace). */
325
+ Entity = "entity",
326
+ /** Instance-level group (one animation instance within an entity). */
327
+ Instance = "instance"
328
+ }
329
+ interface AnimatableGroupInfo {
330
+ /** Stable group UUID used to associate related simple animatables. */
331
+ id: string;
332
+ /** Composite group type for editor grouping semantics. */
333
+ type: AnimatableGroupType;
334
+ /** Optional axis label (x/y/z or r/p/y) for disambiguation. */
335
+ axis?: string;
336
+ /** Optional ordering hint within the group. */
337
+ order?: number;
338
+ }
339
+ type AnimatableValue = AnimatableBoolean | AnimatableNumber | AnimatableString;
340
+ /**
341
+ * @deprecated Complex animatables should be flattened into scalar values.
342
+ */
343
+ type DeprecatedComplexAnimatableValue = AnimatableVector3 | AnimatableVector2 | AnimatableEuler | AnimatableColor;
268
344
  /**
269
345
  * A specification for an animated numerical value
270
346
  *
@@ -284,6 +360,7 @@ interface AnimatableBoolean {
284
360
  frequency?: number;
285
361
  };
286
362
  pub?: AnimatablePubInfo;
363
+ group?: AnimatableGroupInfo;
287
364
  }
288
365
  /**
289
366
  * A specification for an animated numerical value
@@ -307,6 +384,7 @@ interface AnimatableNumber {
307
384
  velocity?: number;
308
385
  };
309
386
  pub?: AnimatablePubInfo;
387
+ group?: AnimatableGroupInfo;
310
388
  }
311
389
  /**
312
390
  * A specification for an animated string value
@@ -327,6 +405,7 @@ interface AnimatableString {
327
405
  length?: number;
328
406
  };
329
407
  pub?: AnimatablePubInfo;
408
+ group?: AnimatableGroupInfo;
330
409
  }
331
410
  /**
332
411
  * A specification for an animated 3-vector value
@@ -425,6 +504,15 @@ declare function instanceOfRawEuler(object: any): object is RawEuler;
425
504
  declare function instanceOfRawColor(object: any): object is RawColor;
426
505
  declare function instanceOfRawRGB(object: any): object is RawRGB;
427
506
  declare function instanceOfRawHSL(object: any): object is RawHSL;
507
+ /**
508
+ * Logs a console warning when a feature callback receives a value of an unexpected type.
509
+ * Use this in `useFeatures` callbacks to surface data-flow issues early.
510
+ *
511
+ * @param feature - The feature key (e.g. "translation.x", "jointValue").
512
+ * @param expected - Human-readable description of the expected type(s) (e.g. "number", "number | string").
513
+ * @param value - The actual value received.
514
+ */
515
+ declare function warnUnexpectedFeatureValue(feature: string, expected: string, value: unknown): void;
428
516
  declare function isRawObject(value: any): boolean;
429
517
 
430
518
  /**
@@ -506,14 +594,13 @@ declare function getHexagonPath(x: number, y: number, size: number, height: numb
506
594
  declare function getDegenerateHexagonPath(x: number, y: number, size: number, height: number): string;
507
595
 
508
596
  /**
509
- * Takes in the completion percentage and the total number of frames in an animation,
510
- * and returns the closest frame as a percentage of the whole animation's duration.
597
+ * Snaps a timestamp in milliseconds to the nearest frame boundary.
511
598
  *
512
- * @param completion - The completion of the animation from 0 to 1.
513
- * @param count - The number of frames in the animation.
514
- * @returns The closest frame number to the completion as a percentage of the total frames.
599
+ * @param stampMs - The timestamp in milliseconds.
600
+ * @param framerate - The framerate (frames per second).
601
+ * @returns The timestamp snapped to the nearest frame boundary in milliseconds.
515
602
  */
516
- declare function closestFrame(completion: number, count: number): number;
603
+ declare function closestFrame(stampMs: number, framerate: number): number;
517
604
 
518
605
  /**
519
606
  * Converts an angle from radians to degrees.
@@ -604,6 +691,44 @@ declare function altColor(color: string, factor: number): string;
604
691
  declare function hexToRgbArray(color: string): [number, number, number];
605
692
  declare function rawRGBToHex({ r, g, b }: RawRGB): string;
606
693
  declare function rawHSLToHex({ h, s, l }: RawHSL): string;
694
+ declare function randomHexString(constrainLuminosity?: number, constrainSaturation?: number): string;
695
+ /**
696
+ * Compute normalized chroma (0..1) from an RGB tuple.
697
+ * - 0 means perfectly gray
698
+ * - 1 means maximum colorfulness
699
+ */
700
+ declare function rgbChroma([r, g, b]: [number, number, number]): number;
701
+ /**
702
+ * Compute lightness (0..1) from an RGB tuple using (max+min)/2.
703
+ * - 0 is black
704
+ * - 1 is white
705
+ */
706
+ declare function rgbLightness([r, g, b]: [number, number, number]): number;
707
+ /**
708
+ * Compute chroma from any CSS color string.
709
+ */
710
+ declare function colorChroma(color: string): number;
711
+ /**
712
+ * Compute lightness from any CSS color string.
713
+ */
714
+ declare function colorLightness(color: string): number;
715
+ interface BlendHeuristicOptions {
716
+ chromaCutoff?: number;
717
+ minLightness?: number;
718
+ maxLightness?: number;
719
+ }
720
+ /**
721
+ * Decide whether mix-blend-difference is likely to improve contrast for a given color.
722
+ * Disables blending for near-neutral, mid-lightness colors (e.g., #737373),
723
+ * where difference blending tends to reduce contrast.
724
+ *
725
+ * Returns false for undefined/unparseable colors as a safe default.
726
+ */
727
+ declare function shouldUseBlendForLabelColor(color?: string | null, opts?: BlendHeuristicOptions): boolean;
728
+ /**
729
+ * Convenience helper to detect a near-neutral, mid-lightness color.
730
+ */
731
+ declare function isNearNeutralMid(color: string, opts?: BlendHeuristicOptions): boolean;
607
732
 
608
733
  /**
609
734
  * Creates a memoized selector function that only updates when the selected value
@@ -780,6 +905,71 @@ declare function rotationMatrixToAngle(R: number[][]): number;
780
905
  * ```
781
906
  */
782
907
  declare function angularDistance(euler1: [number, number, number], euler2: [number, number, number]): number;
908
+ /**
909
+ * Converts Euler angles (ZYX order) to a quaternion.
910
+ *
911
+ * @param euler - Array of three numbers representing rotation angles in radians [z, y, x]
912
+ * @returns A quaternion as [w, x, y, z]
913
+ */
914
+ declare function eulerToQuaternion(euler: [number, number, number]): [number, number, number, number];
915
+ /**
916
+ * Converts a quaternion to Euler angles (ZYX order).
917
+ *
918
+ * @param q - A quaternion as [w, x, y, z]
919
+ * @returns Euler angles as [z, y, x] in radians
920
+ */
921
+ declare function quaternionToEuler(q: [number, number, number, number]): [number, number, number];
922
+ /**
923
+ * Multiplies two quaternions.
924
+ *
925
+ * @param q1 - First quaternion as [w, x, y, z]
926
+ * @param q2 - Second quaternion as [w, x, y, z]
927
+ * @returns The product quaternion as [w, x, y, z]
928
+ */
929
+ declare function quaternionMultiply(q1: [number, number, number, number], q2: [number, number, number, number]): [number, number, number, number];
930
+ /**
931
+ * Calculates the conjugate of a quaternion (inverse for unit quaternions).
932
+ *
933
+ * @param q - A quaternion as [w, x, y, z]
934
+ * @returns The conjugate quaternion as [w, -x, -y, -z]
935
+ */
936
+ declare function quaternionConjugate(q: [number, number, number, number]): [number, number, number, number];
937
+ /**
938
+ * Performs spherical linear interpolation between two quaternions.
939
+ *
940
+ * @param q1 - First quaternion as [w, x, y, z]
941
+ * @param q2 - Second quaternion as [w, x, y, z]
942
+ * @param t - Interpolation factor. Values between 0-1 interpolate, outside this range extrapolate.
943
+ * t=0 returns q1, t=1 returns q2, t<0 extrapolates backwards, t>1 extrapolates forwards.
944
+ * @returns The interpolated/extrapolated quaternion as [w, x, y, z]
945
+ */
946
+ declare function quaternionSlerp(q1: [number, number, number, number], q2: [number, number, number, number], t: number): [number, number, number, number];
947
+ /**
948
+ * Performs spherical linear interpolation between two Euler angles.
949
+ *
950
+ * @param euler1 - First Euler angle [z, y, x] in radians
951
+ * @param euler2 - Second Euler angle [z, y, x] in radians
952
+ * @param t - Interpolation factor. Values between 0-1 interpolate, outside this range extrapolate.
953
+ * t=0 returns euler1, t=1 returns euler2, t<0 extrapolates backwards, t>1 extrapolates forwards.
954
+ * @returns The interpolated/extrapolated Euler angle [z, y, x] in radians
955
+ *
956
+ * @example
957
+ * ```typescript
958
+ * const from = [0, 0, 0];
959
+ * const to = [Math.PI, Math.PI/2, 0];
960
+ * const halfway = eulerSlerp(from, to, 0.5); // 50% interpolation
961
+ * const extrapolated = eulerSlerp(from, to, 1.5); // 150% - extrapolates beyond 'to'
962
+ * const backwards = eulerSlerp(from, to, -0.5); // Extrapolates backwards from 'from'
963
+ * ```
964
+ */
965
+ declare function eulerSlerp(euler1: [number, number, number], euler2: [number, number, number], t: number): [number, number, number];
966
+ /**
967
+ * Calculates the euler angle that represents the rotation matrix from one euler angle to another.
968
+ * @param euler1 - The first euler angle [z, y, x] in radians (source orientation)
969
+ * @param euler2 - The second euler angle [z, y, x] in radians (destination orientation)
970
+ * @return The euler angle representing the rotation from euler1 to euler2 [z, y, x] in radians
971
+ */
972
+ declare function eulerRotationBetween(euler1: [number, number, number], euler2: [number, number, number]): [number, number, number];
783
973
 
784
974
  /**
785
975
  * Merges overlapping segments in an array of intervals.
@@ -1001,20 +1191,243 @@ declare function composeProgressiveResult<T extends {
1001
1191
  */
1002
1192
  declare const EMAIL: RegExp;
1003
1193
 
1194
+ /**
1195
+ * Filter function type for map comparison
1196
+ */
1197
+ type MapFilterFn<K, V> = (key: K, value: V) => boolean;
1198
+ /**
1199
+ * Options for map deep equality comparison
1200
+ */
1201
+ interface MapDeepEqualOptions<K, V> {
1202
+ /** Filter function to determine which entries to compare */
1203
+ filter?: MapFilterFn<K, V>;
1204
+ }
1004
1205
  /**
1005
1206
  * Compares two maps for deep equality by checking all key/value pairs.
1207
+ * Optionally filters which entries to consider in the comparison.
1006
1208
  *
1007
1209
  * @param map1 - The first map to compare.
1008
1210
  * @param map2 - The second map to compare.
1009
- * @returns True if both maps have the same entries; false otherwise.
1211
+ * @param options - Optional configuration for the comparison
1212
+ * @returns True if both maps have the same entries (after filtering); false otherwise.
1010
1213
  *
1011
1214
  * @example
1012
1215
  * ```typescript
1013
- * const mapA = new Map([["key1", { a: 1 }]]);
1014
- * const mapB = new Map([["key1", { a: 1 }]]);
1216
+ * const mapA = new Map([["key1", { a: 1 }], ["key2", { b: 2 }]]);
1217
+ * const mapB = new Map([["key1", { a: 1 }], ["key2", { b: 2 }]]);
1015
1218
  * console.log(isMapDeepEqual(mapA, mapB)); // true
1219
+ *
1220
+ * // With filter - only compare entries where key starts with "key1"
1221
+ * const mapC = new Map([["key1", { a: 1 }], ["key2", { b: 3 }]]);
1222
+ * const mapD = new Map([["key1", { a: 1 }], ["key2", { b: 2 }]]);
1223
+ * console.log(isMapDeepEqual(mapC, mapD, {
1224
+ * filter: (key) => key.toString().startsWith("key1")
1225
+ * })); // true (only key1 is compared)
1226
+ *
1227
+ * // Filter by value properties
1228
+ * const mapE = new Map([["a", { priority: 1, data: "x" }], ["b", { priority: 2, data: "y" }]]);
1229
+ * const mapF = new Map([["a", { priority: 1, data: "z" }], ["b", { priority: 2, data: "y" }]]);
1230
+ * console.log(isMapDeepEqual(mapE, mapF, {
1231
+ * filter: (key, value) => value.priority === 2
1232
+ * })); // true (only compares entries with priority: 2)
1016
1233
  * ```
1017
1234
  */
1018
- declare function isMapDeepEqual<K, V>(map1: Map<K, V>, map2: Map<K, V>): boolean;
1235
+ declare function isMapDeepEqual<K, V>(map1: Map<K, V>, map2: Map<K, V>, options?: MapDeepEqualOptions<K, V>): boolean;
1236
+
1237
+ declare function storedEulerXYZtoRPY(robotData: any): any;
1238
+
1239
+ /**
1240
+ * Configuration for a context provider
1241
+ */
1242
+ interface ContextConfig<T> {
1243
+ /** The React context */
1244
+ context: Context<T | null>;
1245
+ /** Default store/value to provide when context is missing */
1246
+ defaultValue: T;
1247
+ /** Name for error messages */
1248
+ name: string;
1249
+ }
1250
+ /**
1251
+ * Props for components that can optionally provide their own context
1252
+ */
1253
+ interface OptionalContextProps {
1254
+ /** Whether to skip providing context (assume parent provides it) */
1255
+ skipContext?: boolean;
1256
+ }
1257
+ /**
1258
+ * Creates a hook that requires context to be present
1259
+ */
1260
+ declare function createContextHook<T>(context: Context<T | null>, name: string): () => T;
1261
+ /**
1262
+ * Creates a component that provides context if not already present
1263
+ */
1264
+ declare function withOptionalContext<P extends object>(CoreComponent: ComponentType<P>, contextConfig: ContextConfig<any>): ComponentType<P & OptionalContextProps>;
1265
+ /**
1266
+ * Composes multiple context providers into a single component
1267
+ */
1268
+ declare function composeContexts(contexts: {
1269
+ Provider: ComponentType<{
1270
+ children: ReactNode;
1271
+ }>;
1272
+ condition?: () => boolean;
1273
+ }[]): ComponentType<{
1274
+ children: ReactNode;
1275
+ }>;
1276
+ /**
1277
+ * Creates a provider component for a context
1278
+ */
1279
+ declare function createContextProvider<T>(context: Context<T | null>, defaultValue: T): ComponentType<{
1280
+ children: ReactNode;
1281
+ value?: T;
1282
+ }>;
1283
+ /**
1284
+ * Utility type for extracting context value type
1285
+ */
1286
+ type ContextValue<T> = T extends Context<infer U> ? U : never;
1287
+ /**
1288
+ * Configuration for creating a complete context solution
1289
+ */
1290
+ interface ContextSolutionConfig<T> {
1291
+ /** The React context */
1292
+ context: Context<T | null>;
1293
+ /** Default store/value */
1294
+ defaultValue: T;
1295
+ /** Name for debugging and errors */
1296
+ name: string;
1297
+ }
1298
+ /**
1299
+ * Creates a complete context solution with consistent patterns
1300
+ */
1301
+ declare function createContextSolution<T>(config: ContextSolutionConfig<T>): {
1302
+ useContext: () => T;
1303
+ Provider: ComponentType<{
1304
+ children: ReactNode;
1305
+ value?: T | undefined;
1306
+ }>;
1307
+ withOptional: <P extends object>(Component: ComponentType<P>) => ComponentType<P & OptionalContextProps>;
1308
+ context: Context<T | null>;
1309
+ };
1310
+ /**
1311
+ * Standard interface for components that might need tooltip context
1312
+ */
1313
+ interface WithTooltipContextProps {
1314
+ /** Skip providing tooltip context (assume parent provides) */
1315
+ skipTooltipContext?: boolean;
1316
+ }
1317
+ /**
1318
+ * Utility for checking if context exists without throwing
1319
+ */
1320
+ declare function useOptionalContext<T>(context: Context<T | null>): T | null;
1321
+ /**
1322
+ * Creates a context checker function
1323
+ */
1324
+ declare function createContextChecker<T>(context: Context<T | null>): () => boolean;
1325
+
1326
+ interface ValuesData {
1327
+ values: Map<string, RawValue | undefined>;
1328
+ velocities: Map<string, RawValue | undefined>;
1329
+ }
1330
+ interface ValuesActions {
1331
+ updateValues: (values: Map<string, RawValue | undefined>) => void;
1332
+ updateVelocities: (velocities: Map<string, RawValue | undefined>) => void;
1333
+ setTrackValue: (trackKey: string, value: RawValue) => void;
1334
+ /** Replace both maps at once (used by derived orchestrator). */
1335
+ replaceAll: (values: Map<string, RawValue | undefined>, velocities: Map<string, RawValue | undefined>) => void;
1336
+ }
1337
+ type ValuesStore = ValuesData & ValuesActions;
1338
+ /**
1339
+ * Merge `updates` into `base`, returning a new Map.
1340
+ * Keys mapped to `undefined` in `updates` are deleted from the result.
1341
+ */
1342
+ declare function mergeValueMaps(base: Map<string, RawValue | undefined>, updates: Map<string, RawValue | undefined>): Map<string, RawValue | undefined>;
1343
+ /**
1344
+ * Standalone Zustand store for high-frequency transient values and velocities.
1345
+ *
1346
+ * **Single source of truth for runtime animatable values.** Scene rendering,
1347
+ * animation editor controls, and device commanders all read from this store.
1348
+ * The studio/animation/scene stores own *structure* (animatables, tracks,
1349
+ * world, namespaces); this store owns *current numeric state*.
1350
+ *
1351
+ * ## Why a separate store
1352
+ * During playback the derived orchestrator writes new interpolated values at
1353
+ * ~40 Hz. A dedicated store with `subscribeWithSelector` means only the
1354
+ * consumers that need per-tick updates re-evaluate; subscribers of the other
1355
+ * stores aren't woken on every tick. `mergeValueMaps` additionally returns
1356
+ * the same Map reference when no values changed, so redundant writes don't
1357
+ * fan out to subscribers.
1358
+ *
1359
+ * ## Key formats
1360
+ * Two key formats coexist in the maps:
1361
+ * - **trackKey** (`namespaceId/instanceId/trackId`) — written by the derived
1362
+ * orchestrator during playback. Only meaningful to the animation system.
1363
+ * - **dotKey** (`namespaceId.animatableId`) — used by scene rendering hooks
1364
+ * and device command subscriptions.
1365
+ *
1366
+ * The values-bridge subscription (in `apps/studio`) converts trackKey entries
1367
+ * into dotKey entries during playback so downstream consumers don't need to
1368
+ * know about the animation layer.
1369
+ *
1370
+ * ## Readers
1371
+ * - `useFeatures`, `useAnimatableSubscription` (`@semio/scene`, `@semio/vizij`)
1372
+ * — scene rendering hooks subscribe to dotKey entries.
1373
+ * - `AnimatableControl`, `AnimatableEditor` (`@semio/animation`) — animation
1374
+ * editor controls subscribe to trackKey or dotKey entries.
1375
+ * - `device-subscriptions.ts` (studio) — outbound device commands and
1376
+ * trajectories read dotKey entries.
1377
+ *
1378
+ * ## Writers
1379
+ * - `setValueDirectAction` (studio) — direct user edits.
1380
+ * - `device-subscriptions.ts` `onData` handler (studio) — incoming device
1381
+ * feedback writes feedback-namespace dotKeys.
1382
+ * - `namespace-subscription.ts` (studio) — writes root-transform dotKeys for
1383
+ * target/feedback/sim instances when entity config changes.
1384
+ * - `init-derived-orchestrator.ts` (`@semio/animation`) — writes trackKey
1385
+ * values during playback.
1386
+ * - `values-bridge-subscription.ts` (studio) — writes converted trackKey →
1387
+ * dotKey entries.
1388
+ *
1389
+ * ## Gizmo writes (override seam)
1390
+ * Scene primitives that write values back (e.g. joint gizmos) go through
1391
+ * `SceneValueWriterContext` from `@semio/scene` — they call
1392
+ * `useSceneValueWriter().setValue(id, namespace, value)` rather than touching
1393
+ * this store directly. The default writer hits `updateValues` here. Studio
1394
+ * overrides the writer to route through its animation-aware `setValueAction`,
1395
+ * which produces a keypoint update or a direct write depending on the entity's
1396
+ * controlMode and selected animation instance. See `packages/scene/README.md`.
1397
+ *
1398
+ * ## Lifecycle
1399
+ * On project load, studio's `loadFromFullContext` calls `replaceAll(new Map(),
1400
+ * new Map())` before applying the new project, so post-load subscribers
1401
+ * (`namespace-subscription`, derived orchestrator) repopulate from a clean
1402
+ * slate. Outside of load the store accumulates dotKey entries as namespaces
1403
+ * and animatables are added; entries for removed animatables can become stale
1404
+ * until cleared.
1405
+ */
1406
+ declare const useValuesStore: zustand.UseBoundStore<Omit<zustand.StoreApi<ValuesStore>, "subscribe"> & {
1407
+ subscribe: {
1408
+ (listener: (selectedState: ValuesStore, previousSelectedState: ValuesStore) => void): () => void;
1409
+ <U>(selector: (state: ValuesStore) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
1410
+ equalityFn?: ((a: U, b: U) => boolean) | undefined;
1411
+ fireImmediately?: boolean;
1412
+ } | undefined): () => void;
1413
+ };
1414
+ }>;
1415
+
1416
+ /**
1417
+ * Type guard for Three.js OrthographicCamera.
1418
+ *
1419
+ * Accepts a broad parameter type to work with both Three.js Camera and
1420
+ * R3F's Camera union (which may resolve from a different @types/three
1421
+ * declaration in monorepo setups).
1422
+ */
1423
+ declare function isOrthographicCamera(camera: object): camera is OrthographicCamera;
1424
+ /**
1425
+ * Type guard for Three.js PerspectiveCamera.
1426
+ *
1427
+ * Accepts a broad parameter type to work with both Three.js Camera and
1428
+ * R3F's Camera union (which may resolve from a different @types/three
1429
+ * declaration in monorepo setups).
1430
+ */
1431
+ declare function isPerspectiveCamera(camera: object): camera is PerspectiveCamera;
1019
1432
 
1020
- export { type AnimatableBoolean, type AnimatableColor, type AnimatableEuler, type AnimatableNumber, type AnimatablePubInfo, type AnimatableString, type AnimatableValue, type AnimatableVector2, type AnimatableVector3, EMAIL, type Player, type PlayerActions, PlayerContext, type PlayerData, PlayerSlice, type PlayerStore, type PlayerStoreGetter, type PlayerStoreSetter, type Progress, type ProgressiveResult, type RawBoolean, type RawColor, type RawEuler, type RawHSL, type RawNumber, type RawRGB, type RawString, type RawTime, type RawValue, type RawVector2, type RawVector3, Result, type StoreSubscribeWithSelector, type Write, alpha, altColor, angularDistance, closestFrame, composeProgress, composeProgressiveResult, downloadBlob, downloadJSONFile, eulerToRotationMatrix, getDegenerateHexagonPath, getHexagonPath, getId, getLookup, getNamespace, hexToRgbArray, hexToRgba, hslToRgba, incrementProgress, instanceOfRawBoolean, instanceOfRawColor, instanceOfRawEuler, instanceOfRawHSL, instanceOfRawNumber, instanceOfRawRGB, instanceOfRawString, instanceOfRawVector2, instanceOfRawVector3, isMapDeepEqual, isRawObject, log, newPlayer, now, numberToDurationString, overlappingSegments, pairwise, parseJSONFileEvent, parseToMapped, pause, play, pointsToPath, rawHSLToHex, rawRGBToHex, reset, rgbToRgba, rotationMatrixToAngle, seek, setBounds, setDuration, setViewport, stringifyMapped, toDegrees, toRadians, update, useDeep, useDeepSelector, useLazy, useMediaQuery, usePlayerStore };
1433
+ export { type AnimatableBoolean, type AnimatableColor, type AnimatableEuler, type AnimatableGroupInfo, AnimatableGroupType, type AnimatableNumber, type AnimatablePubInfo, type AnimatableString, type AnimatableValue, type AnimatableVector2, type AnimatableVector3, type BlendHeuristicOptions, type ContextConfig, type ContextSolutionConfig, type ContextValue, type DeprecatedComplexAnimatableValue, EMAIL, type MapDeepEqualOptions, type MapFilterFn, type OptionalContextProps, type Player, type PlayerActions, PlayerContext, type PlayerData, PlayerDirection, PlayerSlice, type PlayerStore, type PlayerStoreGetter, type PlayerStoreSetter, type Progress, type ProgressiveResult, type RawBoolean, type RawColor, type RawEuler, type RawHSL, type RawNumber, type RawRGB, type RawString, type RawTime, type RawValue, type RawVector2, type RawVector3, Result, type StoreSubscribeWithSelector, type ValuesActions, type ValuesData, type ValuesStore, type WithTooltipContextProps, type Write, alpha, altColor, angularDistance, closestFrame, colorChroma, colorLightness, composeContexts, composeProgress, composeProgressiveResult, createContextChecker, createContextHook, createContextProvider, createContextSolution, downloadBlob, downloadJSONFile, eulerRotationBetween, eulerSlerp, eulerToQuaternion, eulerToRotationMatrix, getDegenerateHexagonPath, getHexagonPath, getId, getLookup, getNamespace, hexToRgbArray, hexToRgba, hslToRgba, incrementProgress, instanceOfRawBoolean, instanceOfRawColor, instanceOfRawEuler, instanceOfRawHSL, instanceOfRawNumber, instanceOfRawRGB, instanceOfRawString, instanceOfRawVector2, instanceOfRawVector3, isMapDeepEqual, isNearNeutralMid, isOrthographicCamera, isPerspectiveCamera, isRawObject, log, mergeValueMaps, newPlayer, now, numberToDurationString, overlappingSegments, pairwise, parseJSONFileEvent, parseToMapped, pause, play, pointsToPath, quaternionConjugate, quaternionMultiply, quaternionSlerp, quaternionToEuler, randomHexString, rawHSLToHex, rawRGBToHex, reset, reversed, rgbChroma, rgbLightness, rgbToRgba, rotationMatrixToAngle, seek, shouldUseBlendForLabelColor, storedEulerXYZtoRPY, stringifyMapped, toDegrees, toRadians, updated, useDeep, useDeepSelector, useLazy, useMediaQuery, useOptionalContext, usePlayerStore, useValuesStore, warnUnexpectedFeatureValue, withBounce, withBounds, withDirection, withDuration, withLooping, withOptionalContext, withSpeed };