@voqalize/avatar 0.2.2 → 0.3.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 (201) hide show
  1. package/LICENSE +21 -661
  2. package/README.md +144 -663
  3. package/client/Avatar.tsx +57 -0
  4. package/client/AvatarClient.ts +523 -0
  5. package/client/arjun.ts +24 -0
  6. package/client/createAvatar.ts +139 -0
  7. package/client/createCanvasAvatar.ts +72 -0
  8. package/client/index.ts +39 -0
  9. package/client/internal.ts +80 -0
  10. package/client/interviewer-female.ts +4 -0
  11. package/client/interviewer-male.ts +4 -0
  12. package/client/ishita.ts +24 -0
  13. package/client/kabir.ts +24 -0
  14. package/client/meera.ts +24 -0
  15. package/client/naina.ts +24 -0
  16. package/client/professional-female-a.ts +4 -0
  17. package/client/professional-female-b.ts +4 -0
  18. package/client/professional-male-a.ts +4 -0
  19. package/client/professional-male-b.ts +4 -0
  20. package/client/react.ts +13 -0
  21. package/client/types.ts +146 -0
  22. package/client/useAvatar.ts +74 -0
  23. package/client/vikram.ts +24 -0
  24. package/dist/Avatar.d.ts +44 -0
  25. package/dist/Avatar.d.ts.map +1 -0
  26. package/{client/dist → dist}/Avatar.js +2 -2
  27. package/dist/Avatar.js.map +1 -0
  28. package/dist/AvatarClient.d.ts +196 -0
  29. package/dist/AvatarClient.d.ts.map +1 -0
  30. package/dist/AvatarClient.js +450 -0
  31. package/dist/AvatarClient.js.map +1 -0
  32. package/dist/arjun.d.ts +6 -0
  33. package/dist/arjun.d.ts.map +1 -0
  34. package/dist/arjun.js +18 -0
  35. package/dist/arjun.js.map +1 -0
  36. package/dist/createAvatar.d.ts +99 -0
  37. package/dist/createAvatar.d.ts.map +1 -0
  38. package/dist/createAvatar.js +75 -0
  39. package/dist/createAvatar.js.map +1 -0
  40. package/dist/createCanvasAvatar.d.ts +22 -0
  41. package/dist/createCanvasAvatar.d.ts.map +1 -0
  42. package/dist/createCanvasAvatar.js +47 -0
  43. package/dist/createCanvasAvatar.js.map +1 -0
  44. package/dist/index.d.ts +30 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +29 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/internal.d.ts +30 -0
  49. package/dist/internal.d.ts.map +1 -0
  50. package/dist/internal.js +33 -0
  51. package/dist/internal.js.map +1 -0
  52. package/dist/interviewer-female.d.ts +4 -0
  53. package/dist/interviewer-female.d.ts.map +1 -0
  54. package/dist/interviewer-female.js +3 -0
  55. package/dist/interviewer-female.js.map +1 -0
  56. package/dist/interviewer-male.d.ts +4 -0
  57. package/dist/interviewer-male.d.ts.map +1 -0
  58. package/dist/interviewer-male.js +3 -0
  59. package/dist/interviewer-male.js.map +1 -0
  60. package/dist/ishita.d.ts +6 -0
  61. package/dist/ishita.d.ts.map +1 -0
  62. package/dist/ishita.js +18 -0
  63. package/dist/ishita.js.map +1 -0
  64. package/dist/kabir.d.ts +6 -0
  65. package/dist/kabir.d.ts.map +1 -0
  66. package/dist/kabir.js +18 -0
  67. package/dist/kabir.js.map +1 -0
  68. package/dist/meera.d.ts +6 -0
  69. package/dist/meera.d.ts.map +1 -0
  70. package/dist/meera.js +18 -0
  71. package/dist/meera.js.map +1 -0
  72. package/dist/naina.d.ts +6 -0
  73. package/dist/naina.d.ts.map +1 -0
  74. package/dist/naina.js +18 -0
  75. package/dist/naina.js.map +1 -0
  76. package/dist/professional-female-a.d.ts +4 -0
  77. package/dist/professional-female-a.d.ts.map +1 -0
  78. package/dist/professional-female-a.js +3 -0
  79. package/dist/professional-female-a.js.map +1 -0
  80. package/dist/professional-female-b.d.ts +4 -0
  81. package/dist/professional-female-b.d.ts.map +1 -0
  82. package/dist/professional-female-b.js +3 -0
  83. package/dist/professional-female-b.js.map +1 -0
  84. package/dist/professional-male-a.d.ts +4 -0
  85. package/dist/professional-male-a.d.ts.map +1 -0
  86. package/dist/professional-male-a.js +3 -0
  87. package/dist/professional-male-a.js.map +1 -0
  88. package/dist/professional-male-b.d.ts +4 -0
  89. package/dist/professional-male-b.d.ts.map +1 -0
  90. package/dist/professional-male-b.js +3 -0
  91. package/dist/professional-male-b.js.map +1 -0
  92. package/dist/react.d.ts +13 -0
  93. package/dist/react.d.ts.map +1 -0
  94. package/dist/react.js +13 -0
  95. package/dist/react.js.map +1 -0
  96. package/dist/types.d.ts +94 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +93 -0
  99. package/dist/types.js.map +1 -0
  100. package/dist/useAvatar.d.ts +39 -0
  101. package/dist/useAvatar.d.ts.map +1 -0
  102. package/dist/useAvatar.js +35 -0
  103. package/dist/useAvatar.js.map +1 -0
  104. package/dist/vikram.d.ts +6 -0
  105. package/dist/vikram.d.ts.map +1 -0
  106. package/dist/vikram.js +18 -0
  107. package/dist/vikram.js.map +1 -0
  108. package/package.json +81 -24
  109. package/src/avatar.d.ts +161 -120
  110. package/src/avatar.js +234 -146
  111. package/src/behavior.d.ts +26 -0
  112. package/src/behavior.js +102 -0
  113. package/src/camera.js +29 -0
  114. package/src/canvas/author/parts/eye.mjs +722 -0
  115. package/src/canvas/author/parts/hand.mjs +1156 -0
  116. package/src/canvas/author/parts/mouth.mjs +741 -0
  117. package/src/canvas/author/parts/nose.mjs +100 -0
  118. package/src/canvas/author/parts/skin-detail.mjs +67 -0
  119. package/src/canvas/author/path.mjs +283 -0
  120. package/src/canvas/author/rig.mjs +405 -0
  121. package/src/canvas/avatars/round/face.d.mts +3 -0
  122. package/src/canvas/avatars/round/face.mjs +1307 -0
  123. package/src/canvas/create-rig.d.ts +15 -0
  124. package/src/canvas/create-rig.js +100 -0
  125. package/src/canvas/data/img/professional-female-a-hair-back.webp +0 -0
  126. package/src/canvas/data/img/professional-female-a-hair-front.webp +0 -0
  127. package/src/canvas/data/img/professional-female-a-top-body.webp +0 -0
  128. package/src/canvas/data/img/professional-female-b-hair-back.webp +0 -0
  129. package/src/canvas/data/img/professional-female-b-hair-front.webp +0 -0
  130. package/src/canvas/data/img/professional-female-b-top-body.webp +0 -0
  131. package/src/canvas/data/img/professional-male-a-hair-back.webp +0 -0
  132. package/src/canvas/data/img/professional-male-a-hair-front.webp +0 -0
  133. package/src/canvas/data/img/professional-male-a-top-body.webp +0 -0
  134. package/src/canvas/data/img/professional-male-b-hair-back.webp +0 -0
  135. package/src/canvas/data/img/professional-male-b-hair-front.webp +0 -0
  136. package/src/canvas/data/img/professional-male-b-top-body.webp +0 -0
  137. package/src/canvas/data/img/round-m3-hair-back.webp +0 -0
  138. package/src/canvas/data/img/round-m3-hair-front.webp +0 -0
  139. package/src/canvas/data/img/round-m3-top-body.webp +0 -0
  140. package/src/canvas/data/img/round-w1-hair-back.webp +0 -0
  141. package/src/canvas/data/img/round-w1-hair-front.webp +0 -0
  142. package/src/canvas/data/img/round-w1-top-body.webp +0 -0
  143. package/src/canvas/data/interviewer-female.rig.json +1 -0
  144. package/src/canvas/data/interviewer-male.rig.json +1 -0
  145. package/src/canvas/data/professional-female-a.rig.json +1 -0
  146. package/src/canvas/data/professional-female-b.rig.json +1 -0
  147. package/src/canvas/data/professional-male-a.rig.json +1 -0
  148. package/src/canvas/data/professional-male-b.rig.json +1 -0
  149. package/src/canvas/src/live.js +508 -0
  150. package/src/canvas/src/render2d.js +218 -0
  151. package/src/canvas/src/rig.js +297 -0
  152. package/src/canvas/src/vocab.js +96 -0
  153. package/src/clips.js +18 -7
  154. package/src/conformance.js +119 -0
  155. package/src/emotions.js +4 -3
  156. package/src/face-core.js +27 -1
  157. package/src/face-myna.d.ts +7 -0
  158. package/src/face-myna.js +192 -131
  159. package/src/face-peep-control-plane.js +167 -0
  160. package/src/face-peep.d.ts +7 -0
  161. package/src/face-peep.js +274 -116
  162. package/src/face-wren.d.ts +7 -0
  163. package/src/face-wren.js +17 -19
  164. package/src/faces.d.ts +12 -0
  165. package/src/faces.js +53 -0
  166. package/src/gaze.js +119 -4
  167. package/src/hand.js +120 -91
  168. package/src/idle.js +18 -101
  169. package/src/interjections.js +156 -31
  170. package/src/params.js +8 -4
  171. package/src/perform.js +7 -9
  172. package/src/rig.d.ts +24 -0
  173. package/src/rig.js +32 -0
  174. package/src/speech-timing.js +23 -0
  175. package/src/visemes.js +28 -12
  176. package/client/dist/Avatar.d.ts +0 -27
  177. package/client/dist/Avatar.d.ts.map +0 -1
  178. package/client/dist/Avatar.js.map +0 -1
  179. package/client/dist/AvatarClient.d.ts +0 -139
  180. package/client/dist/AvatarClient.d.ts.map +0 -1
  181. package/client/dist/AvatarClient.js +0 -223
  182. package/client/dist/AvatarClient.js.map +0 -1
  183. package/client/dist/index.d.ts +0 -22
  184. package/client/dist/index.d.ts.map +0 -1
  185. package/client/dist/index.js +0 -22
  186. package/client/dist/index.js.map +0 -1
  187. package/client/dist/types.d.ts +0 -86
  188. package/client/dist/types.d.ts.map +0 -1
  189. package/client/dist/types.js +0 -31
  190. package/client/dist/types.js.map +0 -1
  191. package/client/dist/useAvatar.d.ts +0 -42
  192. package/client/dist/useAvatar.d.ts.map +0 -1
  193. package/client/dist/useAvatar.js +0 -51
  194. package/client/dist/useAvatar.js.map +0 -1
  195. package/client/src/Avatar.tsx +0 -33
  196. package/client/src/AvatarClient.ts +0 -270
  197. package/client/src/index.ts +0 -22
  198. package/client/src/types.ts +0 -104
  199. package/client/src/useAvatar.ts +0 -85
  200. package/docs/contract-avatar.md +0 -371
  201. package/docs/contract-protocol.md +0 -461
@@ -1,139 +0,0 @@
1
- /**
2
- * AvatarClient — the avatar's server-message dispatcher, turn clock, and cue
3
- * splice, framework-free (no React; the hook and component wrap this).
4
- *
5
- * ## Turn clock anchoring
6
- *
7
- * A turn's `t0` is anchored to `performance.now()` **at the moment this client
8
- * receives the `{cmd:"speech", event:"start"}` message** — cues are
9
- * client-anchored, not per-cue server-released. That message rides the RTVI
10
- * data channel, ahead of the jitter-buffered audio path, so the residual error
11
- * lands on the video-leads side — the side `docs/contract-protocol.md` says
12
- * perceptual tolerance favours (+125 ms vs -45 ms).
13
- *
14
- * We investigated anchoring on pipecat client-js's own `RTVIEvent
15
- * .BotStartedSpeaking`/`BotStoppedSpeaking` instead (or as a refinement) and
16
- * chose not to, for two reasons:
17
- *
18
- * 1. **No turn correlation.** Those events carry no payload — no `ctx` — so
19
- * there is no way to tell which turn a firing belongs to. Our own
20
- * `speech` command carries `ctx`, which the splice logic below needs
21
- * regardless, so anchoring off it costs nothing extra.
22
- * 2. **Same source, same path, no accuracy gain.** The `AvatarProcessor`
23
- * sits between the TTS service and the output transport and observes the
24
- * transport's own `BotStarted/StoppedSpeakingFrame` broadcasts — the exact
25
- * frame pipecat's built-in speaking detection is *also* driven from. Both
26
- * notifications travel the same data-channel path to the browser. There is
27
- * no local "truly audible now" signal cheaply available: the audio arrives
28
- * on a `MediaStreamTrack` whose only lifecycle events (`unmute`/`mute`)
29
- * fire once per call, not per utterance. Tapping the decoded remote audio
30
- * with a WebAudio `AnalyserNode` RMS gate *would* give one, but it adds
31
- * its own onset latency and a real audio pipeline to build and tune, and
32
- * it would eat into the intentional video-first safety margin rather than
33
- * improve it. Left as a documented option, not built.
34
- *
35
- * `attach()` therefore subscribes to exactly one pipecat event,
36
- * `serverMessage`. It used to also subscribe to both speaking events to report
37
- * a diagnostic drift between our anchor and pipecat's; that hook is gone with
38
- * the rest of the observability surface (`docs/removed.md` § Client callbacks).
39
- *
40
- * ## Cue splice
41
- *
42
- * The widget has two cue-track primitives: `speak({cues, clock})` (a full
43
- * replace) and `pushCues(cues)` (a pure union that can only grow the track,
44
- * never shrink it). Neither is "discard queued cues at or after `from_ms`,
45
- * then append" on its own — `pushCues` has no way to drop a stale tail. So
46
- * this client keeps the turn's canonical cue array itself (kept portion +
47
- * every appended chunk, spliced on each `cues` message) and picks the cheapest
48
- * widget call that stays correct:
49
- *
50
- * - if the splice's `from_ms` doesn't reach back into anything already
51
- * queued — the common case past a turn's first sentence, since only the
52
- * first sentence genuinely plays fast-leg cues — nothing needs discarding:
53
- * `pushCues(newCues)` is the cheap, correct append.
54
- * - if it does reach back (a real fast→accurate splice), `pushCues` cannot
55
- * express the discard; we call `speak()` again with the full spliced
56
- * canonical array on the turn's original clock. `speak()` is otherwise
57
- * documented as also killing an in-flight spoken interjection and
58
- * re-entering `SPEAKING` — both harmless mid-splice (an interjection
59
- * should not be running while a server track owns the mouth; re-entering
60
- * an unchanged state is a no-op past the profile/gaze reset the widget
61
- * already does for a same-name `setState`).
62
- *
63
- * Cues commonly arrive **before** `speech start` — the fast leg starts the
64
- * moment a sentence is handed to TTS, well before `BotStartedSpeakingFrame`.
65
- * Chunks that arrive before the clock is anchored are spliced into the
66
- * canonical array but not yet handed to the widget; `speech start` hands over
67
- * whatever has accumulated as the turn's first `speak()` call. So "the first
68
- * chunk of a turn starts speak()" means the first *widget* call, not
69
- * necessarily the first *message*.
70
- */
71
- import type { PipecatClient } from "@pipecat-ai/client-js";
72
- import type { AvatarApi } from "../../src/avatar.js";
73
- import { type AvatarCommand, type AvatarCue } from "./types.js";
74
- /**
75
- * Internal. Not exported from the package — see `index.ts` for the public
76
- * surface, which is `<Avatar>` and nothing else.
77
- *
78
- * There is deliberately no `accept` predicate here any more. Avatar commands
79
- * travel in one envelope, `{type:"avatar"}`, in both directions and from every
80
- * source: a `AvatarProcessor` in the pipeline and a brain driving the face
81
- * out of band emit the same shape. A per-deployment predicate meant the
82
- * library could not state what an avatar message *is*, which is the one thing
83
- * a wire format has to be able to say. See docs/removed.md § The accept
84
- * predicate.
85
- */
86
- export interface AvatarClientOptions {
87
- /** A dispatch threw (e.g. an unknown state or interjection id, which the
88
- * widget throws on). Defaults to `console.warn`. */
89
- onError?: (err: unknown, msg: AvatarCommand) => void;
90
- /** Override for tests. Defaults to `performance.now`. */
91
- now?: () => number;
92
- }
93
- /**
94
- * The one `RTVIEvent` member `attach()` subscribes to, spelled as its value.
95
- *
96
- * Written out rather than imported because that enum was this module's *only*
97
- * runtime reference to `@pipecat-ai/client-js`, and one runtime reference makes
98
- * the whole `/pipecat` subpath fail to load without the peer installed — even
99
- * for a host that drives `dispatch()` from its own transport and never calls
100
- * `attach()`. The peer is declared optional; this is what makes that true
101
- * rather than aspirational.
102
- *
103
- * String enums are nominal in TypeScript, so the compiler cannot check these
104
- * against the real ones from a type-only import. `client/test/AvatarClient.test.ts`
105
- * does it instead, against the actual enum — the devDependency is present
106
- * exactly where the check belongs and absent from what we ship.
107
- */
108
- export declare const RTVI_EVENTS: {
109
- readonly serverMessage: "serverMessage";
110
- };
111
- export declare class AvatarClient {
112
- private readonly avatar;
113
- private readonly opts;
114
- private readonly now;
115
- private turn;
116
- constructor(avatar: AvatarApi, opts?: AvatarClientOptions);
117
- /** The active turn's ctx, or `null` between turns. For tests and telemetry. */
118
- get turnCtx(): string | null;
119
- /** The active turn's canonical (already-spliced) cue track. For tests and telemetry. */
120
- get turnCues(): AvatarCue[];
121
- /** Dispatch one server message. Anything that isn't in the avatar envelope
122
- * is not ours and is ignored; so is an envelope carrying a `cmd` this build
123
- * has never heard of, per the wire protocol's forward-compat rule. */
124
- dispatch(raw: unknown): void;
125
- private handleState;
126
- private ensureTurn;
127
- private handleCues;
128
- private handleSpeech;
129
- /**
130
- * Subscribe to a live `PipecatClient`'s server messages and dispatch the
131
- * avatar commands among them — the ones in the protocol's own
132
- * `{type:"avatar"}` envelope, which `isAvatarMessage` is the definition of.
133
- * Never throws on a malformed or irrelevant message.
134
- *
135
- * @returns an unsubscribe function; call it on unmount or disconnect.
136
- */
137
- attach(client: PipecatClient): () => void;
138
- }
139
- //# sourceMappingURL=AvatarClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AvatarClient.d.ts","sourceRoot":"","sources":["../src/AvatarClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,SAAS,EAIf,MAAM,YAAY,CAAC;AAYpB;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,mBAAmB;IAClC;wDACoD;IACpD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACrD,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;CAEmB,CAAC;AAU5C,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,IAAI,CAAqB;gBAErB,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,mBAAwB;IAM7D,+EAA+E;IAC/E,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAE3B;IAED,wFAAwF;IACxF,IAAI,QAAQ,IAAI,SAAS,EAAE,CAE1B;IAED;;0EAEsE;IACtE,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI;IAkC5B,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,YAAY;IAkBpB;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,IAAI;CAM1C"}
@@ -1,223 +0,0 @@
1
- /**
2
- * AvatarClient — the avatar's server-message dispatcher, turn clock, and cue
3
- * splice, framework-free (no React; the hook and component wrap this).
4
- *
5
- * ## Turn clock anchoring
6
- *
7
- * A turn's `t0` is anchored to `performance.now()` **at the moment this client
8
- * receives the `{cmd:"speech", event:"start"}` message** — cues are
9
- * client-anchored, not per-cue server-released. That message rides the RTVI
10
- * data channel, ahead of the jitter-buffered audio path, so the residual error
11
- * lands on the video-leads side — the side `docs/contract-protocol.md` says
12
- * perceptual tolerance favours (+125 ms vs -45 ms).
13
- *
14
- * We investigated anchoring on pipecat client-js's own `RTVIEvent
15
- * .BotStartedSpeaking`/`BotStoppedSpeaking` instead (or as a refinement) and
16
- * chose not to, for two reasons:
17
- *
18
- * 1. **No turn correlation.** Those events carry no payload — no `ctx` — so
19
- * there is no way to tell which turn a firing belongs to. Our own
20
- * `speech` command carries `ctx`, which the splice logic below needs
21
- * regardless, so anchoring off it costs nothing extra.
22
- * 2. **Same source, same path, no accuracy gain.** The `AvatarProcessor`
23
- * sits between the TTS service and the output transport and observes the
24
- * transport's own `BotStarted/StoppedSpeakingFrame` broadcasts — the exact
25
- * frame pipecat's built-in speaking detection is *also* driven from. Both
26
- * notifications travel the same data-channel path to the browser. There is
27
- * no local "truly audible now" signal cheaply available: the audio arrives
28
- * on a `MediaStreamTrack` whose only lifecycle events (`unmute`/`mute`)
29
- * fire once per call, not per utterance. Tapping the decoded remote audio
30
- * with a WebAudio `AnalyserNode` RMS gate *would* give one, but it adds
31
- * its own onset latency and a real audio pipeline to build and tune, and
32
- * it would eat into the intentional video-first safety margin rather than
33
- * improve it. Left as a documented option, not built.
34
- *
35
- * `attach()` therefore subscribes to exactly one pipecat event,
36
- * `serverMessage`. It used to also subscribe to both speaking events to report
37
- * a diagnostic drift between our anchor and pipecat's; that hook is gone with
38
- * the rest of the observability surface (`docs/removed.md` § Client callbacks).
39
- *
40
- * ## Cue splice
41
- *
42
- * The widget has two cue-track primitives: `speak({cues, clock})` (a full
43
- * replace) and `pushCues(cues)` (a pure union that can only grow the track,
44
- * never shrink it). Neither is "discard queued cues at or after `from_ms`,
45
- * then append" on its own — `pushCues` has no way to drop a stale tail. So
46
- * this client keeps the turn's canonical cue array itself (kept portion +
47
- * every appended chunk, spliced on each `cues` message) and picks the cheapest
48
- * widget call that stays correct:
49
- *
50
- * - if the splice's `from_ms` doesn't reach back into anything already
51
- * queued — the common case past a turn's first sentence, since only the
52
- * first sentence genuinely plays fast-leg cues — nothing needs discarding:
53
- * `pushCues(newCues)` is the cheap, correct append.
54
- * - if it does reach back (a real fast→accurate splice), `pushCues` cannot
55
- * express the discard; we call `speak()` again with the full spliced
56
- * canonical array on the turn's original clock. `speak()` is otherwise
57
- * documented as also killing an in-flight spoken interjection and
58
- * re-entering `SPEAKING` — both harmless mid-splice (an interjection
59
- * should not be running while a server track owns the mouth; re-entering
60
- * an unchanged state is a no-op past the profile/gaze reset the widget
61
- * already does for a same-name `setState`).
62
- *
63
- * Cues commonly arrive **before** `speech start` — the fast leg starts the
64
- * moment a sentence is handed to TTS, well before `BotStartedSpeakingFrame`.
65
- * Chunks that arrive before the clock is anchored are spliced into the
66
- * canonical array but not yet handed to the widget; `speech start` hands over
67
- * whatever has accumulated as the turn's first `speak()` call. So "the first
68
- * chunk of a turn starts speak()" means the first *widget* call, not
69
- * necessarily the first *message*.
70
- */
71
- import { isAvatarMessage, } from "./types.js";
72
- /**
73
- * The one `RTVIEvent` member `attach()` subscribes to, spelled as its value.
74
- *
75
- * Written out rather than imported because that enum was this module's *only*
76
- * runtime reference to `@pipecat-ai/client-js`, and one runtime reference makes
77
- * the whole `/pipecat` subpath fail to load without the peer installed — even
78
- * for a host that drives `dispatch()` from its own transport and never calls
79
- * `attach()`. The peer is declared optional; this is what makes that true
80
- * rather than aspirational.
81
- *
82
- * String enums are nominal in TypeScript, so the compiler cannot check these
83
- * against the real ones from a type-only import. `client/test/AvatarClient.test.ts`
84
- * does it instead, against the actual enum — the devDependency is present
85
- * exactly where the check belongs and absent from what we ship.
86
- */
87
- export const RTVI_EVENTS = {
88
- serverMessage: "serverMessage",
89
- };
90
- /** Defensive unwrap for the `RTVIEvent.ServerMessage` `{ data }` quirk: some
91
- * transports deliver the payload directly and some wrap it once more. */
92
- function unwrapServerMessage(raw) {
93
- const obj = (raw ?? {});
94
- const inner = obj["data"];
95
- return inner && "type" in inner ? inner : obj;
96
- }
97
- export class AvatarClient {
98
- avatar;
99
- opts;
100
- now;
101
- turn = null;
102
- constructor(avatar, opts = {}) {
103
- this.avatar = avatar;
104
- this.opts = opts;
105
- this.now = opts.now ?? (() => performance.now());
106
- }
107
- /** The active turn's ctx, or `null` between turns. For tests and telemetry. */
108
- get turnCtx() {
109
- return this.turn?.ctx ?? null;
110
- }
111
- /** The active turn's canonical (already-spliced) cue track. For tests and telemetry. */
112
- get turnCues() {
113
- return this.turn ? [...this.turn.cues] : [];
114
- }
115
- /** Dispatch one server message. Anything that isn't in the avatar envelope
116
- * is not ours and is ignored; so is an envelope carrying a `cmd` this build
117
- * has never heard of, per the wire protocol's forward-compat rule. */
118
- dispatch(raw) {
119
- if (!isAvatarMessage(raw))
120
- return;
121
- const msg = raw;
122
- try {
123
- switch (msg.cmd) {
124
- case "state":
125
- this.handleState(msg);
126
- break;
127
- case "interject":
128
- this.avatar.interject(msg.id);
129
- break;
130
- case "gesture":
131
- this.avatar.gesture(msg.id);
132
- break;
133
- case "cues":
134
- this.handleCues(msg);
135
- break;
136
- case "speech":
137
- this.handleSpeech(msg);
138
- break;
139
- case "user":
140
- this.avatar.setUserSpeaking(msg.speaking);
141
- break;
142
- // No default: an unknown `cmd` is a newer server talking to an older
143
- // widget, and the protocol's forward-compat rule says ignore it. There
144
- // is no callback for it — a hook nobody could act on is observability,
145
- // not an interface (`docs/removed.md` § Client callbacks).
146
- }
147
- }
148
- catch (err) {
149
- if (this.opts.onError)
150
- this.opts.onError(err, msg);
151
- else
152
- console.warn("[avatar] dispatch failed", msg, err);
153
- }
154
- }
155
- handleState(msg) {
156
- // Deliberately no client-side dedup: pass every `state` command straight
157
- // through. The widget's own setState already no-ops the parts that matter
158
- // for an unchanged name (`changed` gates the blink and the 'state' event in
159
- // avatar.js), and a server resending the same state name as a
160
- // keepalive/resync must still land so an `emotion`/`gaze` override on this
161
- // particular message takes effect.
162
- this.avatar.setState(msg.name, { emotion: msg.emotion, gaze: msg.gaze });
163
- }
164
- ensureTurn(ctx) {
165
- if (!this.turn || this.turn.ctx !== ctx) {
166
- // A different ctx supersedes whatever turn we had — a stale trailing
167
- // message for the old ctx will find `this.turn.ctx !== ctx` in
168
- // handleSpeech's stop-guard and be ignored, rather than cutting off the
169
- // new turn.
170
- this.turn = { ctx, cues: [], started: false, clock: null };
171
- }
172
- return this.turn;
173
- }
174
- handleCues(msg) {
175
- const turn = this.ensureTurn(msg.ctx);
176
- const kept = turn.cues.filter((c) => c.t < msg.from_ms);
177
- const discarded = turn.cues.length - kept.length;
178
- turn.cues = [...kept, ...msg.cues].sort((a, b) => a.t - b.t);
179
- if (!turn.started) {
180
- // No clock yet — buffer. `speech start` will hand this over as the turn's
181
- // first speak() call.
182
- return;
183
- }
184
- if (discarded === 0) {
185
- this.avatar.pushCues(msg.cues);
186
- }
187
- else {
188
- this.avatar.speak({ cues: turn.cues, clock: turn.clock });
189
- }
190
- }
191
- handleSpeech(msg) {
192
- if (msg.event === "start") {
193
- const turn = this.ensureTurn(msg.ctx);
194
- const t0 = this.now();
195
- const clock = () => this.now() - t0;
196
- turn.clock = clock;
197
- turn.started = true;
198
- this.avatar.speak({ cues: turn.cues, clock });
199
- return;
200
- }
201
- // "stop": only act if it names the turn we're actually riding. A stale stop
202
- // for an already-superseded ctx must not cut off a newer turn.
203
- if (this.turn && this.turn.ctx === msg.ctx) {
204
- this.avatar.stopSpeaking();
205
- this.turn = null;
206
- }
207
- }
208
- /**
209
- * Subscribe to a live `PipecatClient`'s server messages and dispatch the
210
- * avatar commands among them — the ones in the protocol's own
211
- * `{type:"avatar"}` envelope, which `isAvatarMessage` is the definition of.
212
- * Never throws on a malformed or irrelevant message.
213
- *
214
- * @returns an unsubscribe function; call it on unmount or disconnect.
215
- */
216
- attach(client) {
217
- const onServerMessage = (raw) => this.dispatch(unwrapServerMessage(raw));
218
- const serverMessage = RTVI_EVENTS.serverMessage;
219
- client.on(serverMessage, onServerMessage);
220
- return () => client.off(serverMessage, onServerMessage);
221
- }
222
- }
223
- //# sourceMappingURL=AvatarClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AvatarClient.js","sourceRoot":"","sources":["../src/AvatarClient.ts"],"names":[],"mappings":"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"}
@@ -1,22 +0,0 @@
1
- /**
2
- * `@voqalize/avatar` — the avatar as one React component.
3
- *
4
- * import { Avatar } from "@voqalize/avatar";
5
- *
6
- * <Avatar client={pipecatClient} className="avatar-tile" />
7
- *
8
- * That is the whole public surface. Put an `AvatarProcessor` in the pipecat
9
- * pipeline (`pip install voqalize-avatar`), drop this component into the bot's
10
- * tile, and the face listens, thinks, claims the floor and lipsyncs what the
11
- * TTS says.
12
- *
13
- * The widget underneath is framework-free, and the dispatcher between it and
14
- * the RTVI data channel is plain TypeScript — but neither is exported. Two
15
- * consumers wanted a call tile, both are React, and a public API is a promise
16
- * we have to keep across versions. `docs/removed.md` lists what used to be
17
- * here and how to get it back if a real third case argues for it.
18
- *
19
- * Peers: `react >= 18` and `@pipecat-ai/client-js`.
20
- */
21
- export { Avatar, type AvatarProps } from "./Avatar.js";
22
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -1,22 +0,0 @@
1
- /**
2
- * `@voqalize/avatar` — the avatar as one React component.
3
- *
4
- * import { Avatar } from "@voqalize/avatar";
5
- *
6
- * <Avatar client={pipecatClient} className="avatar-tile" />
7
- *
8
- * That is the whole public surface. Put an `AvatarProcessor` in the pipecat
9
- * pipeline (`pip install voqalize-avatar`), drop this component into the bot's
10
- * tile, and the face listens, thinks, claims the floor and lipsyncs what the
11
- * TTS says.
12
- *
13
- * The widget underneath is framework-free, and the dispatcher between it and
14
- * the RTVI data channel is plain TypeScript — but neither is exported. Two
15
- * consumers wanted a call tile, both are React, and a public API is a promise
16
- * we have to keep across versions. `docs/removed.md` lists what used to be
17
- * here and how to get it back if a real third case argues for it.
18
- *
19
- * Peers: `react >= 18` and `@pipecat-ai/client-js`.
20
- */
21
- export { Avatar } from "./Avatar.js";
22
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,MAAM,EAAoB,MAAM,aAAa,CAAC"}
@@ -1,86 +0,0 @@
1
- /**
2
- * types.ts — the avatar wire vocabulary, client side.
3
- *
4
- * The binding definition is `docs/contract-protocol.md`; this file is its
5
- * TypeScript restatement and must not drift from it. The Python half of the
6
- * same vocabulary is `py/src/voqalize_avatar/messages.py` — the three are
7
- * maintained together, and a command added to one without the others is
8
- * incomplete.
9
- *
10
- * A server pushes these as RTVI `server-message`s under the envelope
11
- * `{ type: "avatar", ...cmd-specific fields }`. {@link AvatarCommand} describes
12
- * what rides inside that envelope; {@link isAvatarMessage} is the envelope
13
- * itself, and is the only definition of "this message is for the avatar" the
14
- * client has. There is no protocol version field — see `docs/removed.md`
15
- * § The `v` field.
16
- */
17
- /** A viseme cue: `t` is a ms offset into the utterance's clock, `v` is a Rhubarb A–H (or X) letter. */
18
- export interface AvatarCue {
19
- t: number;
20
- v: string;
21
- i?: number;
22
- }
23
- export interface AvatarStateCmd {
24
- cmd: "state";
25
- name: string;
26
- emotion?: string;
27
- gaze?: string;
28
- }
29
- export interface AvatarInterjectCmd {
30
- cmd: "interject";
31
- id: string;
32
- }
33
- /**
34
- * A hand gesture — the hand at the frame edge plus its face half. Separate from
35
- * `interject` on purpose: `interject("WAVE")` is the face alone and always was,
36
- * so a server that upgrades gets no hand until it asks for one.
37
- */
38
- export interface AvatarGestureCmd {
39
- cmd: "gesture";
40
- id: string;
41
- }
42
- export interface AvatarCuesCmd {
43
- cmd: "cues";
44
- ctx: string;
45
- /** Discard queued cues at or after this offset (ms), then append `cues`. */
46
- from_ms: number;
47
- cues: AvatarCue[];
48
- /**
49
- * True on the one chunk that completes this turn's track: the TTS context is
50
- * closed, so no further chunk will splice into `ctx`. What a client may
51
- * assume, exactly — nothing about playout. The audio it describes is still
52
- * ahead, and `speech stop` remains the end of the turn. It is safe to release
53
- * per-turn cue state (the splice buffer for `ctx`) once the last cue has
54
- * played, and safe to stop expecting more.
55
- *
56
- * Absent on an interrupted turn, deliberately: a turn that was cut never
57
- * claims to have completed. Absent chunks are the normal case — the widget's
58
- * own track already completes on the trailing `X`, so ignoring `final`
59
- * entirely is a correct implementation.
60
- */
61
- final?: boolean;
62
- }
63
- export interface AvatarSpeechCmd {
64
- cmd: "speech";
65
- event: "start" | "stop";
66
- ctx: string;
67
- }
68
- export interface AvatarUserCmd {
69
- cmd: "user";
70
- speaking: boolean;
71
- }
72
- export type AvatarCommand = AvatarStateCmd | AvatarInterjectCmd | AvatarGestureCmd | AvatarCuesCmd | AvatarSpeechCmd | AvatarUserCmd;
73
- /** The full server-message payload: the envelope plus its command. */
74
- export type AvatarServerMessage = AvatarCommand & {
75
- type: "avatar";
76
- };
77
- /**
78
- * Is this server-message payload the avatar's? The envelope is the whole
79
- * answer: `{type:"avatar"}` with a string `cmd`. It used to be a per-deployment
80
- * `accept` predicate on the client, which meant the library could not state
81
- * what an avatar message *is* — see `docs/removed.md` § The accept predicate.
82
- */
83
- export declare function isAvatarMessage(msg: unknown): msg is AvatarServerMessage;
84
- /** The envelope `type` the protocol reserves for avatar traffic. */
85
- export declare const AVATAR_MESSAGE_TYPE = "avatar";
86
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,uGAAuG;AACvG,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,WAAW,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,aAAa,CAAC;AAElB,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,mBAAmB,CAIxE;AAED,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,WAAW,CAAC"}
@@ -1,31 +0,0 @@
1
- /**
2
- * types.ts — the avatar wire vocabulary, client side.
3
- *
4
- * The binding definition is `docs/contract-protocol.md`; this file is its
5
- * TypeScript restatement and must not drift from it. The Python half of the
6
- * same vocabulary is `py/src/voqalize_avatar/messages.py` — the three are
7
- * maintained together, and a command added to one without the others is
8
- * incomplete.
9
- *
10
- * A server pushes these as RTVI `server-message`s under the envelope
11
- * `{ type: "avatar", ...cmd-specific fields }`. {@link AvatarCommand} describes
12
- * what rides inside that envelope; {@link isAvatarMessage} is the envelope
13
- * itself, and is the only definition of "this message is for the avatar" the
14
- * client has. There is no protocol version field — see `docs/removed.md`
15
- * § The `v` field.
16
- */
17
- /**
18
- * Is this server-message payload the avatar's? The envelope is the whole
19
- * answer: `{type:"avatar"}` with a string `cmd`. It used to be a per-deployment
20
- * `accept` predicate on the client, which meant the library could not state
21
- * what an avatar message *is* — see `docs/removed.md` § The accept predicate.
22
- */
23
- export function isAvatarMessage(msg) {
24
- if (typeof msg !== "object" || msg === null)
25
- return false;
26
- const m = msg;
27
- return m.type === AVATAR_MESSAGE_TYPE && typeof m.cmd === "string";
28
- }
29
- /** The envelope `type` the protocol reserves for avatar traffic. */
30
- export const AVATAR_MESSAGE_TYPE = "avatar";
31
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA2EH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,OAAO,CAAC,CAAC,IAAI,KAAK,mBAAmB,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC;AACrE,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC"}
@@ -1,42 +0,0 @@
1
- /**
2
- * useAvatar — mount the widget, wire it to a live session, dispatch its
3
- * server-messages, and clean up.
4
- *
5
- * Internal: `<Avatar>` is the only thing the package exports. It is a separate
6
- * module anyway because the two lifecycles genuinely differ — mounting the
7
- * widget happens once (an avatar swap remounts by design; see the note in the
8
- * effect), while attaching to the pipecat client re-runs whenever the client
9
- * identity changes, which a session reconnect makes it do.
10
- */
11
- import type { PipecatClient } from "@pipecat-ai/client-js";
12
- import { type AvatarApi } from "../../src/avatar.js";
13
- export interface UseAvatarOptions {
14
- /** Which face. Omit for the widget's own default. */
15
- avatar?: string;
16
- /** The live `PipecatClient` to dispatch server-messages from, or `null`
17
- * before connect. `useAvatar` (dis)connects the subscription as this
18
- * changes; it does not create or own the client. */
19
- client?: PipecatClient | null;
20
- }
21
- /**
22
- * The mount ref's type, written out rather than named as React's `RefObject`.
23
- *
24
- * React 18 and 19 declare that alias with different type arguments — 18's
25
- * `useRef<T>(null)` yields `RefObject<T>`, 19's yields `RefObject<T | null>` —
26
- * and because both are the *same alias*, TypeScript compares them by variance
27
- * and rejects whichever one we didn't pick. An anonymous shape forces a
28
- * structural comparison instead, which both versions satisfy, and which the
29
- * `ref` prop accepts on both. This is the only place the 18-vs-19 split shows
30
- * up in the binding; keep it that way.
31
- */
32
- export type AvatarMountRef = {
33
- current: HTMLDivElement | null;
34
- };
35
- export interface UseAvatarHandle {
36
- /** Attach to the mount element: `<div ref={containerRef} />`. */
37
- containerRef: AvatarMountRef;
38
- /** The live widget instance once mounted, else `null`. */
39
- avatar: AvatarApi | null;
40
- }
41
- export declare function useAvatar(options?: UseAvatarOptions): UseAvatarHandle;
42
- //# sourceMappingURL=useAvatar.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useAvatar.d.ts","sourceRoot":"","sources":["../src/useAvatar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGnE,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;wDAEoD;IACpD,MAAM,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,YAAY,EAAE,cAAc,CAAC;IAC7B,0DAA0D;IAC1D,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,eAAe,CAuCzE"}
@@ -1,51 +0,0 @@
1
- /**
2
- * useAvatar — mount the widget, wire it to a live session, dispatch its
3
- * server-messages, and clean up.
4
- *
5
- * Internal: `<Avatar>` is the only thing the package exports. It is a separate
6
- * module anyway because the two lifecycles genuinely differ — mounting the
7
- * widget happens once (an avatar swap remounts by design; see the note in the
8
- * effect), while attaching to the pipecat client re-runs whenever the client
9
- * identity changes, which a session reconnect makes it do.
10
- */
11
- import { useEffect, useRef, useState } from "react";
12
- import { createAvatar } from "../../src/avatar.js";
13
- import { AvatarClient } from "./AvatarClient.js";
14
- export function useAvatar(options = {}) {
15
- const containerRef = useRef(null);
16
- const [avatar, setAvatar] = useState(null);
17
- const avatarClientRef = useRef(null);
18
- // Latest-options ref, so the mount effect (which runs once) still reads the
19
- // live props without re-subscribing.
20
- const optionsRef = useRef(options);
21
- optionsRef.current = options;
22
- useEffect(() => {
23
- const mount = containerRef.current;
24
- if (!mount)
25
- return;
26
- const instance = createAvatar({ mount, avatar: optionsRef.current.avatar });
27
- avatarClientRef.current = new AvatarClient(instance);
28
- setAvatar(instance);
29
- return () => {
30
- instance.destroy();
31
- avatarClientRef.current = null;
32
- setAvatar(null);
33
- };
34
- // Mount once. `avatar` is read at mount time only — the widget has no
35
- // hot-swap-avatar API (`createFace` runs once per mount), so changing it
36
- // re-renders nothing here by design; a caller that needs a different face
37
- // remounts with a `key` prop (see the component's doc).
38
- // eslint-disable-next-line react-hooks/exhaustive-deps
39
- }, []);
40
- useEffect(() => {
41
- const wrapper = avatarClientRef.current;
42
- const pipecatClient = options.client;
43
- if (!wrapper || !pipecatClient)
44
- return;
45
- return wrapper.attach(pipecatClient);
46
- // Re-subscribe when the widget mounts or the session's client changes.
47
- // eslint-disable-next-line react-hooks/exhaustive-deps
48
- }, [avatar, options.client]);
49
- return { containerRef, avatar };
50
- }
51
- //# sourceMappingURL=useAvatar.js.map