npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wix/ai-assistant-avatar

v0.42.0

Published

`@wix/ai-assistant-avatar` is a React component library that provides an animated avatar (Astro) for AI assistant interfaces. The avatar uses `@wix/aria-ai-avatar` to provide smooth, interactive animations including eye tracking, state transitions, and mo

Readme

@wix/ai-assistant-avatar Usage Guide   

Overview

@wix/ai-assistant-avatar is a React component library that provides an animated avatar (Astro) for AI assistant interfaces. The avatar uses @wix/aria-ai-avatar to provide smooth, interactive animations including eye tracking, state transitions, and movement animations.

Key Features

  • Animated Avatar Component: Animated character with multiple states powered by @wix/aria-ai-avatar
  • Eye Tracking: Automatic eye movement following mouse cursor or typing position
  • State Management: Multiple animation states (idle, loading, thinking, etc.)
  • Smooth Transitions: Coordinated animations for moving between UI positions
  • Imperative API: Full control via ref methods for programmatic animation control

Installation

yarn add @wix/ai-assistant-avatar

Peer Dependencies

  • react: ^16.8.0 or higher
  • react-dom: ^16.8.0 or higher

Runtime Dependencies

The package includes these dependencies (automatically installed):

  • @wix/aria-ai-avatar: Avatar animation runtime
  • classnames: CSS class name utilities

Basic Usage

import React, { useRef } from 'react';
import { AiAssistantAvatar, AstroRef } from '@wix/ai-assistant-avatar';

function MyComponent() {
  const avatarRef = useRef<AstroRef>(null);

  return (
    <div>
      <AiAssistantAvatar ref={avatarRef} />
      <button onClick={() => avatarRef.current?.trigger.idle()}>
        Reset to Idle
      </button>
    </div>
  );
}

API Reference

Component Props (AstroProps)

| Prop | Type | Default | Description | |------|------|---------|-------------| | mode | 'light' \| 'dark' | lib default | Color mode of the avatar | | variant | '2d' \| '3d' | lib default | Visual variant of the avatar |

The avatar uses fixed dimensions of 94x94 pixels (defined in ASTRO_SIZE).

Ref API (AstroRef)

The component exposes an imperative handle via ref:

Animation Triggers

trigger: {
  idle: () => Promise<void>;           // Switch to idle state
  undo: () => Promise<void>;           // Undo event animation
  ideaSpark: () => Promise<void>;      // Idea spark event animation
  boredom: () => Promise<void>;        // Toggle between SLEEPING and IDLE states
  bigLoader: () => Promise<void>;      // Toggle between BIG_LOADER and IDLE states
  smallLoader: () => Promise<void>;    // Toggle to SMALL_LOADER (from SMALL state)
  shrink: () => Promise<void>;         // Toggle between SMALL and IDLE states
  publish: () => Promise<void>;        // Publish event animation
}

Visibility & Interaction

hide: () => void;      // Hide the avatar
show: () => void;      // Show the avatar
onMouseMove: (clientX: number, clientY: number) => void;  // Track mouse for eye movement
onUserTyping: (caretX: number, caretY: number) => void;  // Track typing position
onTravelHomeFrom: (x: number, y: number, options: MoveOptions) => Promise<void>;  // Animate from offset TO home position
onTravelAwayTo: (x: number, y: number, options: MoveOptions) => Promise<void>;  // Animate from home TO offset position
changeAstroColor: (color: string) => void;  // Change avatar color using any valid CSS color string (e.g. '#ff0000', 'rgb(255,0,0)')
toggleAvatarBackground: () => void;  // Toggle avatar background visibility

State Properties

avatarHidden: boolean;              // Whether the avatar is hidden
getState: () => AvatarState;        // Get current animation state
wrapper: HTMLDivElement | null;     // DOM wrapper element

Examples

Example 1: State-Based Chat Avatar

Complete example showing how to control avatar animations based on chat state, with mouse tracking:

import React, { useRef, useEffect } from 'react';
import { AiAssistantAvatar, AstroRef } from '@wix/ai-assistant-avatar';

function ChatAvatar({
  isLoading,
  hasNewMessage
}: {
  isLoading: boolean;
  hasNewMessage: boolean;
}) {
  const avatarRef = useRef<AstroRef>(null);

  // Update avatar state based on chat status
  useEffect(() => {
    if (isLoading) {
      avatarRef.current?.trigger.bigLoader();
    } else if (hasNewMessage) {
      avatarRef.current?.trigger.ideaSpark();
      setTimeout(() => avatarRef.current?.trigger.idle(), 1000);
    } else {
      avatarRef.current?.trigger.idle();
    }
  }, [isLoading, hasNewMessage]);

  // Enable eye tracking with mouse movement
  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      avatarRef.current?.onMouseMove(e.clientX, e.clientY);
    };
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  return <AiAssistantAvatar ref={avatarRef} />;
}

Example 2: Single Instance with Portal Pattern

For complex UIs where the avatar moves between positions (input box, messages, welcome screen), use a single avatar instance with React portals:

import React, { useRef, useState, useCallback, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { AiAssistantAvatar, AstroRef, AvatarState } from '@wix/ai-assistant-avatar';

function AvatarManager() {
  const avatarRef = useRef<AstroRef>(null);
  const inputSlotRef = useRef<HTMLDivElement>(null);
  const messageSlotRef = useRef<HTMLDivElement>(null);

  const [activeSlot, setActiveSlot] = useState<HTMLDivElement | null>(null);
  const [previousPosition, setPreviousPosition] = useState<DOMRect | null>(null);
  const [moveOptions, setMoveOptions] = useState({ endState: AvatarState.IDLE });

  // Animate when slot changes
  useEffect(() => {
    if (!avatarRef.current || !activeSlot) return;

    const currentRect = avatarRef.current.wrapper?.getBoundingClientRect();
    if (currentRect) {
      const deltaX = previousPosition ? previousPosition.x - currentRect.x : 0;
      const deltaY = previousPosition ? previousPosition.y - currentRect.y : 0;

      avatarRef.current.onTravelHomeFrom(deltaX, deltaY, moveOptions).then(() => {
        setPreviousPosition(currentRect);
      });
    }
  }, [activeSlot]);

  const moveToMessage = useCallback(() => {
    if (!messageSlotRef.current) return;

    // Capture current position before moving
    const currentRect = avatarRef.current?.wrapper?.getBoundingClientRect();
    if (currentRect) setPreviousPosition(currentRect);

    setMoveOptions({ endState: AvatarState.SMALL_LOADER });
    setActiveSlot(messageSlotRef.current);
  }, []);

  const moveToInput = useCallback(() => {
    if (!inputSlotRef.current) return;

    const currentRect = avatarRef.current?.wrapper?.getBoundingClientRect();
    if (currentRect) setPreviousPosition(currentRect);

    setMoveOptions({ endState: AvatarState.IDLE });
    setActiveSlot(inputSlotRef.current);
  }, []);

  return (
    <>
      {/* Slot placeholders - avatar will portal into the active one */}
      <div ref={inputSlotRef} />
      <div ref={messageSlotRef} />

      {/* Single avatar instance rendered via portal */}
      {activeSlot && createPortal(
        <AiAssistantAvatar ref={avatarRef} />,
        activeSlot
      )}

      <button onClick={moveToMessage}>Move to Message</button>
      <button onClick={moveToInput}>Move to Input</button>
    </>
  );
}

Example 3: Typing Position Tracking

Track user's typing position so avatar eyes follow the caret:

function InputWithAvatar() {
  const avatarRef = useRef<AstroRef>(null);
  const inputRef = useRef<HTMLInputElement>(null);

  const handleInputChange = () => {
    const input = inputRef.current;
    if (!input || !avatarRef.current) return;

    // Get caret position
    const selectionStart = input.selectionStart || 0;
    const rect = input.getBoundingClientRect();

    // Calculate approximate caret position
    const textBeforeCaret = input.value.substring(0, selectionStart);
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    if (context) {
      context.font = getComputedStyle(input).font;
      const textWidth = context.measureText(textBeforeCaret).width;
      const caretX = rect.left + textWidth;
      const caretY = rect.top + rect.height / 2;

      // Avatar eyes will follow the caret
      avatarRef.current.onUserTyping(caretX, caretY);
    }
  };

  return (
    <div>
      <input ref={inputRef} onChange={handleInputChange} />
      <AiAssistantAvatar ref={avatarRef} />
    </div>
  );
}

Configuration Constants

Animation States (AvatarState)

The AvatarState enum is exported from @wix/ai-assistant-avatar:

import { AvatarState } from '@wix/ai-assistant-avatar';

AvatarState.IDLE          // Default idle state
AvatarState.SMALL         // Shrunk/minimized state
AvatarState.SMALL_LOADER  // Small loading indicator
AvatarState.BIG_LOADER    // Large loading indicator
AvatarState.SLEEPING      // Boredom/sleeping state

Size Constants (ASTRO_SIZE)

ASTRO_SIZE = {
  WIDTH: 94,
  HEIGHT: 94,
}

Advanced Patterns

Visibility Control

Control avatar visibility programmatically:

// Hide/show avatar
avatarRef.current?.hide();
avatarRef.current?.show();

// Check if hidden
const isHidden = avatarRef.current?.avatarHidden;

Background Toggle

Toggle avatar background visibility:

avatarRef.current?.toggleAvatarBackground();

Best Practices

  1. Single Instance with Portals: Use a single avatar instance with createPortal for moving between UI positions
  2. Animation Coordination: Use the appropriate travel method based on direction:
    • onTravelHomeFrom(x, y) - Avatar arriving: animates FROM offset TO home position
    • onTravelAwayTo(x, y) - Avatar departing: animates FROM home TO offset position
  3. Performance: Control visibility with hide()/show() methods
  4. State Management: Use AvatarState enum instead of magic strings
  5. Error Handling: Wrap animation calls in try-catch blocks

Troubleshooting

Avatar not appearing: Ensure ref is initialized and the portal target element exists in the DOM

Animations not triggering: Verify ref is attached and methods are called after mount

Eye tracking not working: Ensure onMouseMove receives viewport coordinates (clientX, clientY)

Position transitions not smooth: Check that onTravelHomeFrom delta coordinates are calculated correctly (previous position minus current position)

Portal not rendering: Ensure activeSlot state is set to a valid DOM element before rendering the portal

TypeScript Support

The package includes full TypeScript definitions:

import type { AstroRef, AstroProps, Position, MoveOptions } from '@wix/ai-assistant-avatar';
import { AvatarState } from '@wix/ai-assistant-avatar';

Additional Resources

  • Example Implementation: See packages/ai-assistant-chat-ui for a complete integration example with the portal pattern