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

@microsoft/voice-widget-ui

v0.2.0

Published

Supported UI (Preact shell) for the Voice Agent Widget SDK. Powers the first-class <voice-agent> one-line embed; customers who want full brand control can instead build their own UI on the headless @microsoft/voice-widget core.

Readme

@microsoft/voice-widget-ui

The supported UI for the Voice Agent Widget SDK — a Preact, Shadow-DOM shell (trigger, expandable panel, status/turn states, orb, mute) plus the mountVoiceAgent orchestrator that binds it to a provider and a session. This is the UI behind the first-class one-line <voice-agent> embed.

Want full brand control instead? Build your own UI on the headless @microsoft/voice-widget core (createVoiceAgent) — the advanced escape hatch.

Install & use

import { mountVoiceAgent } from "@microsoft/voice-widget-ui";
import "@microsoft/voice-widget-provider-voicelive"; // self-registers the "voicelive" provider

const controller = mountVoiceAgent(document.getElementById("assistant")!, {
  provider: "voicelive",
  config: { targetType: "model", model: "gpt-realtime" },
  authEndpoint: "https://your-broker.example.com/session", // provider-specific session grant
  ui: {
    variant: "compact",
    placement: "bottom-right",
    showTranscript: true,
    textInput: true,
  },
});
// Session: controller.start() / stop() / destroy() / getState()
// Typed turn: controller.sendText("Hello")
// Live UI: controller.setLangStrings() / setTheme() / setAccentColor()

For the declarative one-liner (<script> + <voice-agent>), use @microsoft/voice-widget-embed, which bundles this package.

This package bundles no provider. Any registered one works — swap the import and the provider name. See Writing a provider.

createWidgetShell(target, opts) is also exported for advanced hosts that want the UI controller without the session orchestration.

ui options

| Option | Meaning | | --- | --- | | variant | bar / compact / full | | placement | bottom-right / bottom-left / top-right / top-left / inline | | startLabel | nonblank trigger text; blank values use the built-in default | | defaultExpanded | start expanded | | muteButton | show the mic mute button — opt-in, off by default | | showTranscript | render the live transcript — opt-in, off by default | | textInput | render the typed-message form — opt-in, off by default; enabled while connected | | theme | auto (default — follows the OS via prefers-color-scheme) / light / dark | | accentColor | any CSS color for the accent; sets both accent stops to that color for a solid fill. Falls back to the brand default if omitted/invalid | | langStrings | Partial<Strings> i18n overrides merged over the built-in English strings (see Internationalization) |

Transcript and typed messages

The built-in transcript and text form are independent opt-ins. Transcript deltas are accumulated into the current agent turn and replaced by the final transcript when it arrives. The shell keeps the latest 100 messages, preserves them when a call stops, and clears them when a new call starts.

const controller = mountVoiceAgent(el, {
  /* session options */
  ui: { showTranscript: true, textInput: true },
  onTranscript: (message) => console.log(message),
});

await controller.start();
controller.sendText("Can you summarize my order?");

sendText accepts nonblank text only while connected and requires a provider that advertises supportsText. Voice Live and the Foundry provider route typed turns through the broker-held Realtime control WebSocket used by the WebRTC session. A typed user turn is emitted through onTranscript immediately because runtimes do not necessarily echo typed input as an audio-transcription event.

Lifecycle and fresh start

The shell surfaces terminal disconnected and error states and changes the call control back to Start a call. Starting again clears the previous transcript and performs a brand-new broker/SDP handshake. Stopping or losing a Voice Live session releases the peer connection, media tracks, audio/level resources, and broker control session. It does not resume the previous conversation.

Theming

The shell is themed through CSS custom properties set on the host element, plus data-* state attributes you can target. These variables and attributes are the supported theming API — the internal .va-* class names are not and may change in any release.

| CSS variable | Default | Role | | --- | --- | --- | | --va-accent | #6d28d9 | Primary accent (trigger, call button) | | --va-accent-2 | #8b5cf6 | Accent gradient stop | | --va-on-accent | #ffffff | Foreground on accent surfaces | | --va-surface | #ffffff | Panel / card background | | --va-fg | #1a1a2e | Primary text | | --va-muted-fg | #6b7280 | Secondary text | | --va-muted | #f3f4f6 | Muted control background | | --va-border | rgba(17, 24, 39, 0.08) | Borders | | --va-error | #ef4444 | Error state | | --va-danger | #e11d48 | Destructive (hang-up / active mute) |

--va-level (0..1) is set at runtime from the agent output level to drive the visualizer — read-only, do not set it. State attributes on the host: data-placement, data-variant, data-status, data-expanded, data-muted, data-reduced-motion, data-theme.

Light / dark / auto

Set the theme option (auto | light | dark, default auto). The shell resolves it to data-theme="light" or data-theme="dark" on the host and sets the CSS color-scheme accordingly; auto follows the OS prefers-color-scheme and updates live. The dark palette overrides the surface/text/border variables under :host([data-theme="dark"]) — accent, error and danger stay shared. Override any --va-* variable yourself to go further, or set accentColor (a shortcut that writes the same value to --va-accent and --va-accent-2) for a solid accent fill:

mountVoiceAgent(el, { /* …session… */, ui: { theme: "dark", accentColor: "#0f6cbd" } });

Update either value later without remounting the widget or interrupting an active session:

controller.setTheme("light");
controller.setAccentColor("#2899f5");
controller.setAccentColor(undefined); // restore the CSS-variable/default palette

Internationalization

All UI strings are externalized and English ships as the reference bundle. Supply your own strings via the langStrings option (or the lang-strings attribute on the <voice-agent> embed) — a flat map merged over the built-in English defaults; unknown keys are ignored and any key you omit falls back to English. A nonblank startLabel still wins the trigger/idle text if both are set.

mountVoiceAgent(el, {
  /* …session… */,
  ui: { langStrings: { idle: "Habla con el asistente", startCall: "Iniciar llamada", endCall: "Colgar" } },
});

Keys: idle, connecting, listening, thinking, speaking, connected, disconnected, error, startCall, endCall, expandAssistant, minimize, mute, unmute, panelLabel, transcriptLabel, userLabel, agentLabel, textInputPlaceholder, sendMessage.

We do not ship first-party translated locale bundles during preview — bring your own translations.

Reacting to your site's locale

The widget does not detect the language itself — your app selects it and passes the matching strings in. Two common approaches:

Pick a bundle by locale. Keep your own per-locale maps and choose one at mount:

const STRINGS: Record<string, Partial<Strings>> = {
  "zh-Hans": { idle: "与助手对话", startCall: "开始通话", endCall: "挂断" },
  "ja": { idle: "アシスタントに話す", startCall: "通話を開始", endCall: "終了" },
};

const locale = document.documentElement.lang; // or your i18n library's active locale
mountVoiceAgent(el, { /* …session… */, ui: { langStrings: STRINGS[locale] } });

Only override the keys you've translated — omitted keys fall back to English, so a partial translation still renders cleanly.

Reuse your existing i18n library. If you already run i18next / FormatJS / etc., map the widget's keys through your own t() so the widget's strings live alongside the rest of your UI:

const langStrings = { idle: t("voiceWidget.idle"), startCall: t("voiceWidget.startCall") /* … */ };
mountVoiceAgent(el, { /* …session… */, ui: { langStrings } });

Live language switches. Replace the current override map without remounting or restarting an active session. Each call resolves from the built-in English defaults, so omitted keys do not retain values from the previous language:

controller.setLangStrings(STRINGS[nextLocale]);
controller.setLangStrings(undefined); // restore English defaults

For the declarative embed, update the lang-strings attribute in place. The theme, accent-color, and lang-strings attributes are observed live; see @microsoft/voice-widget-embed.

Language, direction, and strings in one call

setUiLocale updates the UI-locale metadata as a unit — language (lang), writing direction (dir), and the string overrides — without remounting or restarting an active session. It is the surface a host drives from its own i18n runtime when switching locale mid-call. Every field is independently optional, so you can update strings alone, direction alone, or all three together:

controller.setUiLocale({
  lang: "ar",
  dir: "rtl",
  strings: { idle: "تحدث إلى المساعد", endCall: "إنهاء المكالمة" },
});

lang and dir are applied to the widget host element (.va-widget-host), so a screen reader announces the new language and the panel lays out in the right direction — while the widget keeps inheriting the page's ambient dir when you don't set one. dir accepts "ltr", "rtl", or "auto"; passing an empty/undefined lang clears it. Strings re-resolve from the English defaults on every call, so an omitted key never keeps the previous language's value. On the declarative embed the equivalent surface is the ui-locale attribute (a JSON object) or the element's setUiLocale() method; see @microsoft/voice-widget-embed.

Public API & stability

Stable (semver-guarded): the exports above, the ui options, the controller's sendText, setLangStrings, setUiLocale, setTheme, and setAccentColor methods, the currently-read <voice-agent> attributes, and the theming contract (--va-* variables + data-* attributes). Internal (may change): .va-* class names, component internals, DOM structure.

Browser support

Evergreen browsers, last 2 stable major versions of Chrome, Edge, and Firefox; Safari 16+ (macOS); iOS Safari 16+; Android Chrome (last 2). Requires WebRTC (RTCPeerConnection), AudioWorklet, Shadow DOM v1 / Custom Elements v1, and ES2020. No IE11 or legacy (EdgeHTML) Edge.

Versioning & support

The package follows semver. During public preview it stays 0.x: a minor may include breaking changes, a patch never does; full semver is committed at 1.0 (GA). We fix a11y, i18n, theming-contract, in-matrix browser-compat, and security issues in the shipped shell. Forks, BYO-UI on the headless core, and customer-written providers are out of support scope.