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

@coreexl/mentor

v0.2.5

Published

A fully customizable, reusable mentor/chat interface component built with React, LiveKit, and TypeScript

Readme

@coreexl/mentor

v0.2.4 — React UI package for SchoolExl's AI mentor — chat, voice, and avatar video powered by LiveKit Agents.

Installation

npm install @coreexl/mentor
# or
pnpm add @coreexl/mentor

No CSS import needed. Styles inject automatically when the component mounts.

If your project uses Tailwind v4, add one @source directive to your globals.css:

@source "../node_modules/@coreexl/mentor/src/**/*.{ts,tsx}";

Components

<MentorPage>

The main all-in-one mentor interface. Renders a chat panel with optional voice and avatar modes.

import { MentorPage } from '@coreexl/mentor'

<MentorPage
  accessKey="your-access-key"
  serverUrl="https://your-server.com"
  agentName="Coach Sarah"
  systemPrompt="You are a helpful tutor..."
  language="en-US"
  gender="female"
/>

Key props:

| Prop | Type | Default | Description | |------|------|---------|-------------| | accessKey | string | required | Server access key | | serverUrl | string | env NEXT_PUBLIC_COREEXL_MENTOR_SERVER | Backend URL | | systemPrompt | string | — | Agent personality / instructions | | agentName | string | "Coach Timothy" | Display name | | agentTitle | string | "Concept Coach" | Subtitle | | language | string | "en-IN" | BCP-47 language code for TTS + STT | | gender | "female" \| "male" | "female" | Voice gender | | voiceName | string | — | Specific Google Chirp3-HD voice name (e.g. "Aoede", "Fenrir") | | ttsProvider | "google" \| "chatterbox" | "google" | TTS engine — Google Chirp3 HD or Chatterbox Turbo (voice cloning) | | chatterboxVoice | string | — | Named voice on the Chatterbox server (e.g. "jamaican_female", "jamaican_male") | | mode | "chat" \| "avatar" | "chat" | UI mode | | videoMode | boolean | false | Enable Ditto avatar video (use with mode="avatar") | | avatarId | string | "imogen" | Avatar ID registered on the Ditto server | | avatarService | "ditto" \| "tavus" \| "bey" | "ditto" | Avatar backend | | overlay | boolean | false | Render as floating draggable overlay widget | | presentContent | ReactNode | — | Content for the Present tab | | userId | string | auto-generated | Stable user identity | | debug | boolean | false | Enable verbose logging | | tools | Tool[] | — | OpenAI-format tool definitions forwarded to the agent | | onToolCall | OnToolCallHandler | — | Handler called when agent invokes a tool | | onConnect | () => void | — | Called on successful connection | | onDisconnect | () => void | — | Called on disconnection |


<AvatarPanel>

A standalone avatar-only component — just the video frame and a connect button.

import { AvatarPanel } from '@coreexl/mentor'

<AvatarPanel
  accessKey="your-access-key"
  serverUrl="https://your-server.com"
  avatarId="imogen"
  language="hi-IN"
  gender="female"
  width={340}
  height={512}
  borderRadius={20}
  resizable
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | accessKey | string | required | Server access key | | serverUrl | string | env | Backend URL | | avatarId | string | "imogen" | Avatar ID | | avatarService | string | "ditto" | Avatar backend | | language | string | "en-IN" | BCP-47 language code | | gender | "female" \| "male" | "female" | Voice gender | | ttsProvider | "google" \| "chatterbox" | "google" | TTS engine | | chatterboxVoice | string | — | Named Chatterbox voice | | systemPrompt | string | — | Agent instructions | | avatarPreviewUrl | string | — | Image shown blurred before connecting | | cameraMode | boolean | false | Enable camera toggle button | | width | string \| number | "100%" | Frame width | | height | string \| number | "100%" | Frame height | | borderRadius | string \| number | 16 | Frame corner radius | | resizable | boolean | false | Show drag-to-resize handle |


TTS Providers

Google Chirp3 HD (default)

High-quality neural voices in 50+ languages. Auto-selects based on language + gender, or pin a specific voice with voiceName.

<MentorPage ttsProvider="google" language="en-US" gender="female" voiceName="Aoede" />

Chatterbox Turbo (voice cloning)

Local GPU-based TTS with voice cloning. Requires a running Chatterbox server configured via CHATTERBOX_URL on the backend.

Supports paralinguistic tags in generated speech: [laughs], [sighs], [excited], [curious], [chuckle], [gasp], [whispers].

<MentorPage ttsProvider="chatterbox" chatterboxVoice="jamaican_female" />

Jamaican voices

When chatterboxVoice contains "jamaican", the agent automatically applies a Jamaican voice style:

  • Authentic expressions: wah gwaan, irie, mi deh yah, bless up, everyting criss, nuh true?
  • Emotion tags placed naturally inline: [laughs], [sighs], [excited]
  • Rhythm punctuation — ellipses for pauses, CAPS for emphasis
  • Short punchy conversational sentences
// Warm Jamaican female mentor
<MentorPage
  ttsProvider="chatterbox"
  chatterboxVoice="jamaican_female"
  agentName="Paulette"
  systemPrompt="You are Paulette, a warm Jamaican tutor..."
/>

// Deep Jamaican male mentor
<MentorPage
  ttsProvider="chatterbox"
  chatterboxVoice="jamaican_male"
  agentName="Denzel"
  gender="male"
  systemPrompt="You are Denzel, a confident Jamaican mentor..."
/>

Uploading a custom voice

POST a WAV or MP3 to your Chatterbox server once — saved permanently by name:

curl -X POST https://your-chatterbox-server/voices/upload \
  -F "name=my_voice" \
  -F "[email protected]"

Then use it: <MentorPage ttsProvider="chatterbox" chatterboxVoice="my_voice" />


Typical layout: Avatar + Chat side by side

import { AvatarPanel, MentorPage } from '@coreexl/mentor'

export default function TutorPage() {
  return (
    <div style={{ display: 'flex', height: '100vh', gap: 16, padding: 16 }}>
      <AvatarPanel
        accessKey={process.env.NEXT_PUBLIC_ACCESS_KEY!}
        serverUrl={process.env.NEXT_PUBLIC_MENTOR_SERVER}
        avatarId="imogen"
        language="en-US"
        gender="female"
        width={340}
        height="100%"
        borderRadius={20}
      />
      <div style={{ flex: 1 }}>
        <MentorPage
          accessKey={process.env.NEXT_PUBLIC_ACCESS_KEY!}
          serverUrl={process.env.NEXT_PUBLIC_MENTOR_SERVER}
          agentName="Coach Sarah"
          language="en-US"
          gender="female"
          mode="chat"
        />
      </div>
    </div>
  )
}

Language Support

| Language | Code | Female Voice | Male Voice | |----------|------|-------------|-----------| | English (India) | en-IN | Despina | Charon | | English (US) | en-US | Puck | Fenrir | | English (UK) | en-GB | Aoede | Charon | | English (Australia) | en-AU | Zephyr | Fenrir | | Hindi | hi-IN | Leda | Orus | | Punjabi | pa-IN | Leda | Orus | | Urdu | ur-IN | Aoede | Charon | | Bengali | bn-IN | Aoede | Orus | | Gujarati | gu-IN | Aoede | Orus | | Kannada | kn-IN | Aoede | Rasalgethi | | Malayalam | ml-IN | Aoede | Charon | | Marathi | mr-IN | Aoede | Iapetus | | Tamil | ta-IN | Aoede | Orus | | Telugu | te-IN | Aoede | Orus | | French | fr-FR | Kore | Orus | | French (Canada) | fr-CA | Aoede | Charon | | German | de-DE | Aoede | Orus | | Spanish | es-ES | Zephyr | Fenrir | | Spanish (US) | es-US | Leda | Fenrir | | Portuguese (Brazil) | pt-BR | Aoede | Orus | | Portuguese (Portugal) | pt-PT | Leda | Charon | | Arabic | ar-XA | Zephyr | Rasalgethi | | Japanese | ja-JP | Autonoe | Fenrir | | Korean | ko-KR | Aoede | Iapetus | | Chinese (Simplified) | zh-CN | Aoede | Gacrux | | Chinese (Traditional) | zh-TW | Leda | Umbriel | | Chinese (Cantonese) | yue-HK | Aoede | Orus | | Italian | it-IT | Aoede | Orus | | Dutch | nl-NL | Aoede | Charon | | Polish | pl-PL | Erinome | Orus | | Russian | ru-RU | Callirrhoe | Fenrir | | Turkish | tr-TR | Aoede | Achird | | Swedish | sv-SE | Aoede | Charon | | Danish | da-DK | Aoede | Orus | | Finnish | fi-FI | Aoede | Charon | | Czech | cs-CZ | Aoede | Algenib | | Indonesian | id-ID | Aoede | Orus | | Thai | th-TH | Aoede | Orus | | Vietnamese | vi-VN | Aoede | Fenrir |


Overlay mode

<MentorPage accessKey="..." overlay agentName="Tutor" />

Renders as a floating minimizable widget in the bottom-right corner.


Tool calling

<MentorPage
  accessKey="..."
  tools={[{
    type: 'function',
    function: {
      name: 'show_hint',
      description: 'Show a hint to the student',
      parameters: {
        type: 'object',
        properties: { hint: { type: 'string' } },
        required: ['hint'],
      },
    },
  }]}
  onToolCall={async (toolCall) => {
    if (toolCall.name === 'show_hint') {
      setHint(toolCall.arguments.hint as string)
      return { success: true }
    }
  }}
/>

Environment variables

| Variable | Description | |----------|-------------| | NEXT_PUBLIC_COREEXL_MENTOR_SERVER | Default backend URL (used if serverUrl prop is omitted) | | NEXT_PUBLIC_ACCESS_KEY | Default access key (used if accessKey prop is omitted) |


Troubleshooting

Black borders on other elements — Tailwind v4 sets border-color: currentColor. Fix:

*, ::before, ::after { border-color: transparent; }

Styles not applying — Add the @source directive (see Installation). Do not import the CSS manually.

Language not supported — Pass a valid BCP-47 code via language (see Language Support table).