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

@eka-care/medassist-widget-embed

v0.2.109

Published

Embeddable MedAssist widget loader built with Web Components.

Downloads

5,988

Readme

@eka-care/medassist-widget-embed

Lightweight, embeddable widget loader for MedAssist using Web Components. Add the script and the <eka-medassist-widget> custom element to any page for a framework-agnostic, lazy-loaded chat widget.

Overview

  • Custom Web Component<eka-medassist-widget> for easy embedding
  • Lazy loading – Widget assets load when the user opens the widget
  • Framework agnostic – No React or build step required
  • Isolated styling – Shadow DOM avoids conflicts with host styles
  • Simple integration – Script tag + custom element

Installation

npm install @eka-care/medassist-widget-embed

Or load from a CDN (replace the version as needed):

<script
  src="https://cdn.jsdelivr.net/npm/@eka-care/medassist-widget-embed@latest/dist/index.js"
  async></script>

For self-hosting, ensure the built widget assets (medassist-widget.js, medassist-widget.css) are served alongside index.js, or use the package’s default CDN for widget assets (see How it works).

Usage

<!DOCTYPE html>
<html>
  <head>
    <title>My Website</title>
  </head>
  <body>
    <h1>Welcome</h1>

    <eka-medassist-widget agent-id="your-agent-id-here"></eka-medassist-widget>

    <script src="https://cdn.jsdelivr.net/npm/@eka-care/medassist-widget-embed@latest/dist/index.js" async></script>
  </body>
</html>

The widget appears as a floating button; clicking it loads and opens the chat.

Configuration

Required attributes

  • agent-id – Your MedAssist agent identifier

Optional attributes

  • icon-url – Custom icon URL for the widget button (default: Eka CDN icon)
  • title – Widget title
  • base-url – Base URL for API/agent config
  • display-mode"widget" (floating button), "full" (inline full view) or "side_panel" (docked panel, see Side panel mode)
  • side-panel-mode"push" (default) or "overlay"; only used with display-mode="side_panel"
  • side-panel-width – panel width in px, clamped to 320–560 (default 400); side panel only
  • side"right" (default) or "left"; defaults to "left" on RTL documents; side panel only
  • context – JSON string of context key-value pairs

Programmatic config with EkaMedAssist.init()

After the script loads, you can set or override config before the user opens the widget:

window.EkaMedAssist.init({
  agentId: "your-agent-id",
  title: "MedAssist",
  iconUrl: "https://example.com/icon.svg",
  baseUrl: "https://api.example.com",
  context: { key: "value" },
  theme: {
    background: "#1a1738",
    primary: "#09FBD3",
    textColor: "white",
  },
});

Custom icon example

<eka-medassist-widget
  agent-id="your-agent-id"
  icon-url="https://example.com/custom-icon.svg"></eka-medassist-widget>

Custom launcher styling

If you need to reposition the floating launcher button (different breakpoints, custom z-index, etc.), pass CSS via customLauncherStyles in init() — or set it directly with the custom-launcher-styles attribute. The CSS is injected into the launcher's Shadow DOM, so you can use media queries, hover states, and anything else CSS allows.

Two selectors are supported as a stable contract:

  • :host — the launcher wrapper. Use this to override position, bottom, right, left, top, transform, z-index, etc.
  • #medassist-open-btn — the button element itself. Use this for size, background, box-shadow, border-radius, etc.
window.EkaMedAssist.init({
  agentId: "your-agent-id",
  customLauncherStyles: `
    @media (max-width: 991px) {
      :host {
        right: auto;
        left: 50%;
        bottom: 47px;
        transform: translateX(-50%);
      }
    }
    @media (max-width: 767px) {
      :host { bottom: 34px; }
    }
  `,
});

This only affects the launcher button — the chat panel that opens on click is unaffected.

Side panel mode

display-mode="side_panel" docks the chat to a viewport edge at a fixed width instead of floating it. On mobile-width viewports the widget falls back to its mobile layout, as with every other mode.

<eka-medassist-widget
  agent-id="your-agent-id"
  display-mode="side_panel"
  side-panel-mode="push"
  side-panel-width="400"
  side="right"></eka-medassist-widget>

side-panel-mode picks how the panel relates to your page:

  • overlay – the panel floats on top of the page. Nothing on your page moves.
  • push (default) – the page is shifted aside so the panel does not cover it. On open the loader injects a stylesheet into your document <head> and adds the eka-medassist-pushed class to <body>; on close both are removed again, leaving the page exactly as it was.

The injected stylesheet does two things:

body.eka-medassist-pushed { margin-right: 400px; transition: margin 300ms ease; } /* margin-left when side="left" */
body.eka-medassist-pushed > * { max-width: 100%; }

The margin reflows normal in-flow content. The max-width cap makes viewport-sized app roots (width: 100vw, Tailwind w-screen) that are direct children of <body> shrink with <body> instead of sliding under the panel. It does not touch position: fixed elements, and a min-width you set still wins.

If your viewport-sized root sits inside a wrapper element, the cap does not reach it. Either size it relative to its parent (w-full instead of w-screen), or subtract the panel width explicitly:

/* An app shell that must stay 100vw when the panel is closed */
.app-shell {
  width: calc(100vw - var(--eka-medassist-panel-width, 0px));
  transition: width 300ms ease;
}

Host analytics events

The widget never talks to GTM / gtag / dataLayer itself. The host page opts in by defining a global window.trackWidgetEvent(name, detail); the widget calls it and the host forwards whatever it wants to its own analytics stack. Without that function the widget emits nothing. detail is { sessionId, ...extra } and carries only non-sensitive identifiers — never message text, user data or phone numbers. sessionId is omitted only while a brand-new session is still being created.

Define the function before the loader script runs: events fired before it exists are dropped, not queued.

Startup (per occurrence)

| name | Extra detail | When | | --- | --- | --- | | auth_failed | { reason, status? } | A credential was rejected: token exchange or refresh failed, or the server answered 401/403. reason is the SDK recovery reason (AUTH_EXPIRED, SESSION_FORBIDDEN) or the server's error code. | | session_failed | { reason, status? } | The session could not be created, resumed or kept: a session API error, or an in-band session_not_found / token-mismatch / not-owned error. reason is the server's error code, the recovery reason (SESSION_NOT_FOUND, RETRY_LATER) or the SDK classification. | | connection_failed | { reason, status?, closeCode? } | The transport could not be reached or dropped: NETWORK_ERROR (fetch failed), timeout (request timed out), CONNECTION_ERROR, or a socket/stream disconnect reason (network_error, server_closed, authentication_error, timeout, max_reconnect_attempts) with the socket close code. | | connection_established | { elapsedMs } | The transport is connected. elapsedMs is measured from the start of the attempt (the widget's start call, or the disconnect that preceded a reconnect). Fires on every reconnect too. | | ready | — | Session established and transport connected — the user can send a message. Once per session id. |

SDK errors are reported only while the chat is not connected; an error after that (feedback, history, recording) is not a startup failure.

Engagement (once per session id)

| name | Extra detail | When | | --- | --- | --- | | typing_started | — | First non-empty keystroke in the composer. Voice transcription does not count. | | first_message_sent | — | The user's first message is sent (Enter or the send button). Not fired when a resumed session already contains a user message. | | first_bot_reply | { messageId } | The first assistant reply starts rendering. The static greeting does not count, and a resumed session with an earlier reply does not re-fire. |

Starting a new chat creates a new session id, so the once-per-session events fire again for it.

Event names are snake_case; the host adds its own prefix when forwarding (e.g. eka_auth_failed).

window.trackWidgetEvent = (name, detail) => {
  window.dataLayer?.push({ event: `eka_${name}`, ...detail });
};

What push cannot move, and the hooks to handle it

position: fixed elements (sticky headers, banners, inset: 0 overlays) are positioned against the viewport, so no outside stylesheet can move them. While the panel is open the loader publishes what it did so your own CSS or JS can follow:

  • body.eka-medassist-pushed – class on <body> while pushed.
  • --eka-medassist-panel-width – custom property on <html>, e.g. 400px. Removed on close.
  • --eka-medassist-panel-side – custom property on <html>, right or left. Removed on close.
  • eka-medassist:side-panel-pushwindow event, detail: { pushed: boolean, width: number, side: "left" | "right" }. Fired on open (pushed: true) and close (pushed: false).
  • window.EkaMedAssist.onSidePanelPushChange(pushed, widthPx, side) – same signal as a callback, alongside EkaMedAssist.onClose.

Give the custom property a 0px fallback so the same rule is correct while the panel is closed:

/* A fixed header that should stop at the panel edge instead of running under it */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: var(--eka-medassist-panel-width, 0px);
  transition: right 300ms ease;
}
window.addEventListener("eka-medassist:side-panel-push", (e) => {
  const { pushed, width, side } = e.detail;
  // e.g. store in app state and offset fixed chrome / recompute a canvas
});

How it works

  1. Initial load – The script registers the custom element <eka-medassist-widget>.
  2. Button – The element shows a floating button (or full view if display-mode="full").
  3. Lazy load – On first open, the script fetches the widget JS and CSS (from the same origin or from https://cdn.jsdelivr.net/npm/@eka-care/medassist-widget@latest/dist/ by default).
  4. Isolation – The widget runs inside Shadow DOM to avoid style and script conflicts.

To use your own widget assets (e.g. after building @eka-care/medassist-widget), host medassist-widget.js and medassist-widget.css and point the script via the data-widget-assets attribute:

<script
  src="/path/to/widget-embed/index.js"
  data-widget-assets="/path/to/widget-assets/"
  async></script>

File structure

widget-embed/
├── dist/
│   ├── index.js        # Loader and custom element
│   ├── iframe.js       # Optional iframe helper
│   └── iframe.html     # Optional iframe template
├── assets/
│   └── bot-icon.svg    # Default button icon (or CDN)
└── README.md

Browser support

  • Chrome, Firefox, Safari, Edge (latest)
  • Requires Custom Elements, Shadow DOM, and ES6+

Development

Build the package:

npm run build

To build and bundle the widget assets locally:

npm run build:with-widget

(Requires the widget package to be built first.)

Integration examples

React

import { useEffect } from "react";

function App() {
  useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://cdn.jsdelivr.net/npm/@eka-care/medassist-widget-embed@latest/dist/index.js";
    script.async = true;
    document.body.appendChild(script);
  }, []);

  return (
    <div>
      <h1>My App</h1>
      <eka-medassist-widget agent-id="your-agent-id" />
    </div>
  );
}

Vue

<template>
  <div>
    <eka-medassist-widget :agent-id="agentId" />
  </div>
</template>

<script>
export default {
  data: () => ({ agentId: "your-agent-id" }),
  mounted() {
    const script = document.createElement("script");
    script.src = "https://cdn.jsdelivr.net/npm/@eka-care/medassist-widget-embed@latest/dist/index.js";
    script.async = true;
    document.body.appendChild(script);
  },
};
</script>

Troubleshooting

  • Widget not appearing – Check agent-id, ensure the loader script loads (Network tab), and check the console for errors.
  • Assets 404 – If using self-hosted assets, ensure medassist-widget.js and medassist-widget.css are served at the path given in data-widget-assets (or use the default CDN).

License

MIT