@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
@@ -0,0 +1,94 @@
1
+ /**
2
+ * types.ts — the avatar wire vocabulary, client side.
3
+ *
4
+ * The binding definition is `docs/contract-wire.md`; this file is its
5
+ * TypeScript restatement and must not drift from it. The Python half of the
6
+ * same vocabulary is `packages/avatar-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 — an unknown `cmd` is simply
15
+ * ignored, which is the whole forward-compatibility rule.
16
+ */
17
+ import type { AvatarActionId, VisemeLetter } from "../src/avatar.js";
18
+ /** A viseme cue: `t` is a ms offset into the utterance's clock, `v` is a Rhubarb A–H (or X) letter. */
19
+ export interface AvatarCue {
20
+ t: number;
21
+ v: VisemeLetter;
22
+ /** 0..1 loudness. Absent means full. */
23
+ i?: number;
24
+ }
25
+ /** A durable, lower-priority server claim. `null` explicitly clears it. */
26
+ export interface AvatarClaimCmd {
27
+ cmd: "claim";
28
+ state: "STRAINING" | "THINKING" | "WORKING" | null;
29
+ }
30
+ /** A self-completing authored sequence: face, body, and optionally a hand. */
31
+ export interface AvatarActionCmd {
32
+ cmd: "action";
33
+ id: AvatarActionId;
34
+ }
35
+ export interface AvatarCuesCmd {
36
+ cmd: "cues";
37
+ ctx: string;
38
+ /** Discard queued cues at or after this offset (ms), then append `cues`. */
39
+ from_ms: number;
40
+ cues: AvatarCue[];
41
+ /**
42
+ * True on the one chunk that completes this turn's track: the TTS context is
43
+ * closed, so no further chunk will splice into `ctx`. What a client may
44
+ * assume, exactly — nothing about playout. The audio it describes is still
45
+ * ahead, and Pipecat's `botStoppedSpeaking` remains the end of the turn. It
46
+ * is safe to stop expecting more cue chunks after `final`.
47
+ *
48
+ * Absent on an interrupted turn, deliberately: a turn that was cut never
49
+ * claims to have completed. Absent chunks are the normal case — the widget's
50
+ * own track already completes on the trailing `X`, so ignoring `final`
51
+ * entirely is a correct implementation.
52
+ */
53
+ final?: boolean;
54
+ }
55
+ export type AvatarCommand = AvatarClaimCmd | AvatarActionCmd | AvatarCuesCmd;
56
+ /** The full server-message payload: the envelope plus its command. */
57
+ export type AvatarServerMessage = AvatarCommand & {
58
+ type: "avatar";
59
+ };
60
+ /** An envelope addressed to the avatar, before its payload has been read. */
61
+ export type AvatarEnvelope = {
62
+ type: "avatar";
63
+ cmd: string;
64
+ } & Record<string, unknown>;
65
+ /**
66
+ * Is this server-message payload the avatar's? The envelope is the whole
67
+ * answer: `{type:"avatar"}` with a string `cmd`. It used to be a per-deployment
68
+ * `accept` predicate on the client, which meant the library could not state
69
+ * what an avatar message *is*.
70
+ *
71
+ * Addressed to us is not the same as understood by us: {@link parseAvatarCommand}
72
+ * is the second half.
73
+ */
74
+ export declare function isAvatarMessage(msg: unknown): msg is AvatarEnvelope;
75
+ /**
76
+ * Read an envelope's payload into the wire vocabulary, or `null` if this build
77
+ * cannot act on it — an unknown `cmd`, an id or claim outside the enum, a
78
+ * malformed `cues` chunk.
79
+ *
80
+ * `null` is the forward-compat rule with a type attached: a newer server
81
+ * talking to an older widget is *expected*, and the older widget ignores what
82
+ * it does not know rather than guessing. Doing the check here rather than at
83
+ * each use site is what lets the wire types be closed unions instead of
84
+ * `string` — the boundary is one function, so it can be the only place that
85
+ * has to be honest about untrusted input.
86
+ *
87
+ * Cues survive individually: one unrecognised letter in a chunk drops that cue,
88
+ * not the utterance around it. Losing a frame of articulation is a far smaller
89
+ * regression than losing a sentence of it.
90
+ */
91
+ export declare function parseAvatarCommand(msg: AvatarEnvelope): AvatarCommand | null;
92
+ /** The envelope `type` the protocol reserves for avatar traffic. */
93
+ export declare const AVATAR_MESSAGE_TYPE = "avatar";
94
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErE,uGAAuG;AACvG,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,wCAAwC;IACxC,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ;AAED,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI,CAAC;CACpD;AAED,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,EAAE,EAAE,cAAc,CAAC;CACpB;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;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,eAAe,GACf,aAAa,CAAC;AAElB,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErE,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAInE;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,aAAa,GAAG,IAAI,CAoC5E;AAED,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,WAAW,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * types.ts — the avatar wire vocabulary, client side.
3
+ *
4
+ * The binding definition is `docs/contract-wire.md`; this file is its
5
+ * TypeScript restatement and must not drift from it. The Python half of the
6
+ * same vocabulary is `packages/avatar-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 — an unknown `cmd` is simply
15
+ * ignored, which is the whole forward-compatibility rule.
16
+ */
17
+ import { ACTION_IDS, VISEME_LETTERS } from "../src/avatar.js";
18
+ /**
19
+ * Is this server-message payload the avatar's? The envelope is the whole
20
+ * answer: `{type:"avatar"}` with a string `cmd`. It used to be a per-deployment
21
+ * `accept` predicate on the client, which meant the library could not state
22
+ * what an avatar message *is*.
23
+ *
24
+ * Addressed to us is not the same as understood by us: {@link parseAvatarCommand}
25
+ * is the second half.
26
+ */
27
+ export function isAvatarMessage(msg) {
28
+ if (typeof msg !== "object" || msg === null)
29
+ return false;
30
+ const m = msg;
31
+ return m.type === AVATAR_MESSAGE_TYPE && typeof m.cmd === "string";
32
+ }
33
+ const ACTIONS = new Set(ACTION_IDS);
34
+ const LETTERS = new Set(VISEME_LETTERS);
35
+ /**
36
+ * Read an envelope's payload into the wire vocabulary, or `null` if this build
37
+ * cannot act on it — an unknown `cmd`, an id or claim outside the enum, a
38
+ * malformed `cues` chunk.
39
+ *
40
+ * `null` is the forward-compat rule with a type attached: a newer server
41
+ * talking to an older widget is *expected*, and the older widget ignores what
42
+ * it does not know rather than guessing. Doing the check here rather than at
43
+ * each use site is what lets the wire types be closed unions instead of
44
+ * `string` — the boundary is one function, so it can be the only place that
45
+ * has to be honest about untrusted input.
46
+ *
47
+ * Cues survive individually: one unrecognised letter in a chunk drops that cue,
48
+ * not the utterance around it. Losing a frame of articulation is a far smaller
49
+ * regression than losing a sentence of it.
50
+ */
51
+ export function parseAvatarCommand(msg) {
52
+ switch (msg.cmd) {
53
+ case "claim": {
54
+ const state = msg.state;
55
+ if (state === null || state === "STRAINING" || state === "THINKING" || state === "WORKING") {
56
+ return { cmd: "claim", state };
57
+ }
58
+ return null;
59
+ }
60
+ case "action":
61
+ return typeof msg.id === "string" && ACTIONS.has(msg.id)
62
+ ? { cmd: "action", id: msg.id }
63
+ : null;
64
+ case "cues": {
65
+ if (typeof msg.ctx !== "string" || !Number.isFinite(msg.from_ms) || !Array.isArray(msg.cues)) {
66
+ return null;
67
+ }
68
+ const cues = [];
69
+ for (const c of msg.cues) {
70
+ if (typeof c !== "object" || c === null)
71
+ continue;
72
+ const { t, v, i } = c;
73
+ if (typeof t !== "number" || !Number.isFinite(t))
74
+ continue;
75
+ if (typeof v !== "string" || !LETTERS.has(v))
76
+ continue;
77
+ cues.push(typeof i === "number" ? { t, v: v, i } : { t, v: v });
78
+ }
79
+ return {
80
+ cmd: "cues",
81
+ ctx: msg.ctx,
82
+ from_ms: msg.from_ms,
83
+ cues,
84
+ ...(msg.final === true ? { final: true } : {}),
85
+ };
86
+ }
87
+ default:
88
+ return null;
89
+ }
90
+ }
91
+ /** The envelope `type` the protocol reserves for avatar traffic. */
92
+ export const AVATAR_MESSAGE_TYPE = "avatar";
93
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * useAvatar — mount an avatar into a ref'd element for as long as there is a
3
+ * client to embody, and tear it down after.
4
+ *
5
+ * Internal; `<Avatar>` is the only thing the React entry exports. The whole
6
+ * hook is one effect, because the factory takes the client at construction:
7
+ * there is no separate attach step to keep in its own lifecycle any more.
8
+ */
9
+ import type { PipecatClient } from "@pipecat-ai/client-js";
10
+ import { type AvatarFactory, type AvatarOptions, type SvgAvatarOptions } from "./createAvatar.js";
11
+ export interface UseAvatarOptions<O extends AvatarOptions = SvgAvatarOptions> {
12
+ /** The live `PipecatClient`, or `null` before connect. Nothing mounts until
13
+ * this is non-null — an avatar with nothing to embody has nothing to do. */
14
+ client?: PipecatClient | null;
15
+ /** The avatar implementation. Defaults to the bundled SVG faces. */
16
+ create?: AvatarFactory<O>;
17
+ /** Implementation options, forwarded verbatim. Read at mount only. */
18
+ options?: Omit<O, keyof AvatarOptions>;
19
+ }
20
+ /**
21
+ * The mount ref's type, written out rather than named as React's `RefObject`.
22
+ *
23
+ * React 18 and 19 declare that alias with different type arguments — 18's
24
+ * `useRef<T>(null)` yields `RefObject<T>`, 19's yields `RefObject<T | null>` —
25
+ * and because both are the *same alias*, TypeScript compares them by variance
26
+ * and rejects whichever one we didn't pick. An anonymous shape forces a
27
+ * structural comparison instead, which both versions satisfy, and which the
28
+ * `ref` prop accepts on both. This is the only place the 18-vs-19 split shows
29
+ * up in the binding; keep it that way.
30
+ */
31
+ export type AvatarMountRef = {
32
+ current: HTMLDivElement | null;
33
+ };
34
+ export interface UseAvatarHandle {
35
+ /** Attach to the mount element: `<div ref={containerRef} />`. */
36
+ containerRef: AvatarMountRef;
37
+ }
38
+ export declare function useAvatar<O extends AvatarOptions = SvgAvatarOptions>({ client, create, options }?: UseAvatarOptions<O>): UseAvatarHandle;
39
+ //# sourceMappingURL=useAvatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAvatar.d.ts","sourceRoot":"","sources":["../client/useAvatar.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,aAAa,GAAG,gBAAgB;IAC1E;gFAC4E;IAC5E,MAAM,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,oEAAoE;IACpE,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,sEAAsE;IACtE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,aAAa,CAAC,CAAC;CACxC;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;CAC9B;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,aAAa,GAAG,gBAAgB,EAClE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAE,gBAAgB,CAAC,CAAC,CAAM,GACpD,eAAe,CAyBjB"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * useAvatar — mount an avatar into a ref'd element for as long as there is a
3
+ * client to embody, and tear it down after.
4
+ *
5
+ * Internal; `<Avatar>` is the only thing the React entry exports. The whole
6
+ * hook is one effect, because the factory takes the client at construction:
7
+ * there is no separate attach step to keep in its own lifecycle any more.
8
+ */
9
+ import { useEffect, useRef } from "react";
10
+ import { createAvatar as createSvgAvatar, } from "./createAvatar.js";
11
+ export function useAvatar({ client, create, options } = {}) {
12
+ const containerRef = useRef(null);
13
+ // Latest-options ref, so the mount effect reads the live values without
14
+ // remounting the face every time a caller passes a fresh object literal.
15
+ const optionsRef = useRef(options);
16
+ optionsRef.current = options;
17
+ useEffect(() => {
18
+ const mount = containerRef.current;
19
+ if (!mount || !client)
20
+ return;
21
+ const factory = (create ?? createSvgAvatar);
22
+ // The cast is the seam between "O minus the two we supply" and O. It is
23
+ // sound by construction and TypeScript cannot see through the spread of a
24
+ // generic; the two halves are typed at the boundary the caller touches.
25
+ const instance = factory({ mount, client, ...optionsRef.current });
26
+ return () => instance.destroy();
27
+ // A new client identity is a new thing to embody, so the avatar is rebuilt
28
+ // rather than re-pointed. Hosts keep one `PipecatClient` across
29
+ // connect/disconnect cycles, so this does not fire on an ordinary
30
+ // reconnect; if yours constructs a fresh client per session, expect the
31
+ // face to remount with it.
32
+ }, [client, create]);
33
+ return { containerRef };
34
+ }
35
+ //# sourceMappingURL=useAvatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAvatar.js","sourceRoot":"","sources":["../client/useAvatar.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EACL,YAAY,IAAI,eAAe,GAIhC,MAAM,mBAAmB,CAAC;AA8B3B,MAAM,UAAU,SAAS,CACvB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAA0B,EAAE;IAErD,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAElD,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC;QACnC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO;QAC9B,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,eAAe,CAAqB,CAAC;QAChE,wEAAwE;QACxE,0EAA0E;QAC1E,wEAAwE;QACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,EAAkB,CAAC,CAAC;QACnF,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QAChC,2EAA2E;QAC3E,gEAAgE;QAChE,kEAAkE;QAClE,wEAAwE;QACxE,2BAA2B;IAC7B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAErB,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Polished professional male avatar, authored to read at call-tile size. */
2
+ import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
3
+ import type { AvatarInstance } from './createAvatar.js';
4
+ export type { CanvasAvatarOptions };
5
+ export declare function createAvatar(options: CanvasAvatarOptions): AvatarInstance;
6
+ //# sourceMappingURL=vikram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vikram.d.ts","sourceRoot":"","sources":["../client/vikram.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAG7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAUxD,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,cAAc,CAOzE"}
package/dist/vikram.js ADDED
@@ -0,0 +1,18 @@
1
+ /** Polished professional male avatar, authored to read at call-tile size. */
2
+ import { createCanvasAvatar } from './createCanvasAvatar.js';
3
+ const RIG_URL = new URL('../src/canvas/data/professional-male-a.rig.json', import.meta.url);
4
+ const IMAGES = {
5
+ 'professional-male-a-top-body.webp': new URL('../src/canvas/data/img/professional-male-a-top-body.webp', import.meta.url),
6
+ 'professional-male-a-hair-back.webp': new URL('../src/canvas/data/img/professional-male-a-hair-back.webp', import.meta.url),
7
+ 'professional-male-a-hair-front.webp': new URL('../src/canvas/data/img/professional-male-a-hair-front.webp', import.meta.url),
8
+ };
9
+ const FACE = () => import('../src/canvas/avatars/round/face.mjs');
10
+ export function createAvatar(options) {
11
+ return createCanvasAvatar(options, {
12
+ rigUrl: RIG_URL,
13
+ images: IMAGES,
14
+ face: FACE,
15
+ label: 'Vikram — polished professional male avatar',
16
+ });
17
+ }
18
+ //# sourceMappingURL=vikram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vikram.js","sourceRoot":"","sources":["../client/vikram.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAI7D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,iDAAiD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5F,MAAM,MAAM,GAAG;IACb,mCAAmC,EAAE,IAAI,GAAG,CAAC,0DAA0D,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACzH,oCAAoC,EAAE,IAAI,GAAG,CAAC,2DAA2D,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IAC3H,qCAAqC,EAAE,IAAI,GAAG,CAAC,4DAA4D,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;CACrH,CAAC;AACX,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC;AAIlE,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,OAAO,kBAAkB,CAAC,OAAO,EAAE;QACjC,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,4CAA4C;KACpD,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@voqalize/avatar",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "A JavaScript-programmable 2-D talking head for AI voice calls: server-synced lipsync, state through motion, zero dependencies.",
5
- "license": "AGPL-3.0-only",
5
+ "license": "MIT",
6
6
  "author": "Voqalize",
7
7
  "homepage": "https://github.com/voqalize/avatar#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/voqalize/avatar.git"
10
+ "url": "git+https://github.com/voqalize/avatar.git",
11
+ "directory": "packages/avatar"
11
12
  },
12
13
  "bugs": {
13
14
  "url": "https://github.com/voqalize/avatar/issues"
@@ -31,26 +32,92 @@
31
32
  },
32
33
  "type": "module",
33
34
  "sideEffects": false,
35
+ "main": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
34
37
  "exports": {
35
38
  ".": {
36
- "types": "./client/dist/index.d.ts",
37
- "default": "./client/dist/index.js"
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "./react": {
43
+ "types": "./dist/react.d.ts",
44
+ "default": "./dist/react.js"
45
+ },
46
+ "./internal": {
47
+ "types": "./dist/internal.d.ts",
48
+ "default": "./dist/internal.js"
49
+ },
50
+ "./faces/peep": {
51
+ "types": "./src/face-peep.d.ts",
52
+ "default": "./src/face-peep.js"
53
+ },
54
+ "./faces/wren": {
55
+ "types": "./src/face-wren.d.ts",
56
+ "default": "./src/face-wren.js"
57
+ },
58
+ "./faces/myna": {
59
+ "types": "./src/face-myna.d.ts",
60
+ "default": "./src/face-myna.js"
61
+ },
62
+ "./avatars/arjun": {
63
+ "types": "./dist/arjun.d.ts",
64
+ "default": "./dist/arjun.js"
65
+ },
66
+ "./avatars/meera": {
67
+ "types": "./dist/meera.d.ts",
68
+ "default": "./dist/meera.js"
69
+ },
70
+ "./avatars/vikram": {
71
+ "types": "./dist/vikram.d.ts",
72
+ "default": "./dist/vikram.js"
73
+ },
74
+ "./avatars/ishita": {
75
+ "types": "./dist/ishita.d.ts",
76
+ "default": "./dist/ishita.js"
77
+ },
78
+ "./avatars/kabir": {
79
+ "types": "./dist/kabir.d.ts",
80
+ "default": "./dist/kabir.js"
81
+ },
82
+ "./avatars/naina": {
83
+ "types": "./dist/naina.d.ts",
84
+ "default": "./dist/naina.js"
85
+ },
86
+ "./avatars/interviewer-male": {
87
+ "types": "./dist/interviewer-male.d.ts",
88
+ "default": "./dist/interviewer-male.js"
89
+ },
90
+ "./avatars/interviewer-female": {
91
+ "types": "./dist/interviewer-female.d.ts",
92
+ "default": "./dist/interviewer-female.js"
93
+ },
94
+ "./avatars/professional-male-a": {
95
+ "types": "./dist/professional-male-a.d.ts",
96
+ "default": "./dist/professional-male-a.js"
97
+ },
98
+ "./avatars/professional-female-a": {
99
+ "types": "./dist/professional-female-a.d.ts",
100
+ "default": "./dist/professional-female-a.js"
101
+ },
102
+ "./avatars/professional-male-b": {
103
+ "types": "./dist/professional-male-b.d.ts",
104
+ "default": "./dist/professional-male-b.js"
105
+ },
106
+ "./avatars/professional-female-b": {
107
+ "types": "./dist/professional-female-b.d.ts",
108
+ "default": "./dist/professional-female-b.js"
38
109
  },
39
110
  "./package.json": "./package.json"
40
111
  },
41
112
  "files": [
42
113
  "src",
43
- "client/dist",
44
- "client/src",
45
- "docs/contract-protocol.md",
46
- "docs/contract-avatar.md",
47
- "README.md"
114
+ "client",
115
+ "dist"
48
116
  ],
49
117
  "scripts": {
50
- "build": "tsc -p client/tsconfig.json",
51
- "typecheck": "tsc -p client/tsconfig.json --noEmit",
52
- "test": "vitest run",
53
- "prepare": "tsc -p client/tsconfig.json"
118
+ "build": "tsc -p tsconfig.json",
119
+ "typecheck": "tsc -p tsconfig.json --noEmit",
120
+ "prepare": "tsc -p tsconfig.json"
54
121
  },
55
122
  "peerDependencies": {
56
123
  "@pipecat-ai/client-js": ">=1.4 <2",
@@ -67,15 +134,5 @@
67
134
  "react-dom": {
68
135
  "optional": true
69
136
  }
70
- },
71
- "devDependencies": {
72
- "@pipecat-ai/client-js": "1.7.0",
73
- "@types/react": "^18.3.3",
74
- "@types/react-dom": "^18.3.0",
75
- "jsdom": "^25.0.1",
76
- "react": "^18.3.1",
77
- "react-dom": "^18.3.1",
78
- "typescript": "^5.7.3",
79
- "vitest": "^3.2.7"
80
137
  }
81
138
  }