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

@meldkit/vue-ui

v0.1.2

Published

Ready-made collaboration UI for @meldkit/vue: presence avatars, connection status, remote carets, live pointer cursors

Readme

@meldkit/vue-ui

Ready-made collaboration UI for @meldkit/vue: presence avatars, a connection indicator, remote carets and live pointer cursors.

Entirely optional. The SDK has no idea this package exists, and everything here is a few dozen lines you could write yourself — the point is that you shouldn't have to write them twice.

npm install @meldkit/vue-ui
<script setup>
import { Avatars, ConnectionStatus } from '@meldkit/vue-ui';
</script>

<template>
  <Avatars />
  <ConnectionStatus />
</template>

Import the stylesheet once, near your app's entry — or from your global CSS:

@import '@meldkit/vue-ui/styles.css';

Components

<Avatars /> — who's in the room, as a row of circles with each person's initials. Reads presence from the room in scope, so it takes no inputs in the common case. Pass me / others to render a list you've already filtered or sorted.

The row is bounded in both directions. Past stackAfter (3) the circles overlap instead of pushing the layout sideways; past max (5) the remainder collapses into a +N whose tooltip names them. A room of thirty people is a real thing, and a presence bar that tries to draw thirty of anything has stopped being a presence bar.

Your own circle leads the row and carries an accent ring, so it never ends up inside the overflow count. A green dot marks anyone whose cursor or pointer says they're active right now.

<ConnectionStatus /> — connection state, and errors. It reports both, because the two disagree in the case that matters: with the wrong room key the socket is perfectly healthy and the document is empty, so a plain status dot would say "connected" next to a blank screen.

<CaretOverlay :text="…"> — remote carets over a text input. A textarea can't contain elements, so this wraps yours in a mirror layer holding the same text with the same font and padding, its own text transparent, sitting on top. The browser works out where character N lands — no measurement code, correct through wrapping and resizing.

<CaretOverlay :text="body.text">
  <textarea :ref="body.bind"></textarea>
</CaretOverlay>

It draws carets and nothing else. The value, the edits and the CRDT stay with useSharedText from @meldkit/vue.

<InlineCarets :text="…" :others="…" /> — the same carets in text you render yourself: a list row, a heading, a cell.

<Cursors :space="{ width, height }" /> — everyone else's pointer, drawn over a shared surface. Reads pointer from presence and places it as a percentage of its container, so the same position lands in the same place in windows of different sizes. Its wrapper has to be positioned, and has to have the same aspect ratio as space.

Motion is the part that takes care. Each cursor moves by transform rather than left/top, because offsets are a layout change and animating them re-lays-out and repaints whatever is underneath on every frame. And each glides for as long as that person's own last gap between updates took, rather than a fixed duration — too short and a cursor sprints to each position then sits still until the next one, which is what reads as jitter; too long and it lags behind the person driving it.

Pass status to put a word next to someone's name — "drawing", "typing" — from whatever field of your presence answers that.

<SetupNotice /> — first-run instructions for when the token endpoint isn't configured yet. Render it in place of your app:

<SetupNotice v-if="meldkit.error?.code === 'config'" />
<Board v-else />

randomIdentity() — a throwaway display name and colour for apps that don't have accounts yet. A const in setup() runs once and stays put, so unlike the React binding there is no hook to wrap it in. Only for display; the userId in your access token is who you are to MeldKit, and it comes from your server.

Presence shape

The components read three fields:

type CollaboratorPresence = {
  name: string;
  color: string;
  cursor?: number | null; // character offset, not a pixel position
  pointer?: { x: number; y: number } | null;
};

A floor, not a ceiling — carry whatever else you need alongside them. An app that adds an editing field naming the row someone is in can point the same caret component at one list row or at a whole document.

Offsets rather than coordinates is the load-bearing decision: a pixel position means nothing in a window of a different width, an offset means the same thing everywhere.

Styling

The class names, custom properties and stylesheet are the same as @meldkit/react-ui's, deliberately: the markup differs between the two packages, the design doesn't. In order of how much you want to change:

  1. Retheme with the custom properties (--mk-accent, --mk-line, --mk-doc-font, …) — set them on :root, or on any ancestor to scope it.
  2. Restyle with your own rules against the mk- class names, or skip the stylesheet entirely and style the markup yourself.

Each component renders one element carrying the class it is styled by, so there is nothing of ours between your CSS and the thing it is styling. A class on the component lands on that element, so your own styling goes alongside.

One caveat, and it's the only part of the CSS that isn't cosmetic: the mirror layer and the text input must agree on every property that affects where a character lands, or carets drift further off with every line. Both read the same --mk-doc-* variables from one rule so they can't diverge. Retheme those rather than setting font or padding on the textarea directly.