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

thinking-orbs-universal

v1.1.0

Published

Framework-agnostic dotted thought-orb loading indicators for AI & agent UIs — with Vue 3, React, Web Components, and Vanilla JS support.

Readme

thinking-orbs-universal

Dotted thought-orb loading indicators for AI & agent UIs — framework-agnostic, with native support for Vue 3, React, Web Components, and Vanilla JS.

A derivative of thinking-orbs by Jakub Antalik. The original is a beautiful, hand-tuned React-only canvas library; this project decouples the rendering engine from any framework and adds Vue 3, Web Component, and Vanilla JS adapters — plus a CSS custom-property API for theme authors.

Six hand-tuned animated states, each shipped at two purpose-tuned sizes, rendered on a plain 2D canvas — no WebGL, no filters, works identically in Chrome, Safari and Firefox.

Repository · Report an issue · Architecture & design spec · Implementation plan

Install

npm install thinking-orbs-universal

vue, react and react-dom are listed as optional peer dependencies — a pure-Vue project will never pull in React, and a plain-HTML project will pull in no framework at all.

Quick start

Pick the adapter that matches your stack. Every adapter renders the exact same pixels.

React

import { ThinkingOrb } from 'thinking-orbs-universal/react';

function Status() {
  return <ThinkingOrb state="searching" size={64} />;
}

Vue 3

<script setup lang="ts">
import { ThinkingOrb } from 'thinking-orbs-universal/vue';
</script>

<template>
  <ThinkingOrb state="working" :size="64" :speed="1.5" />
</template>

Web Component / plain HTML

The custom element auto-registers on import:

<script type="module">
  import 'thinking-orbs-universal/web-component';
</script>

<thinking-orb state="searching" size="20"></thinking-orb>

Attributes mirror the props: state, size, theme, speed, paused, aria-label. Updates to attributes are reflected live.

Vanilla JS

For frameworks not listed above (Svelte, Angular, Solid…) or direct canvas control:

import { mountOrb } from 'thinking-orbs-universal';

const orb = mountOrb(document.getElementById('orb')!, {
  state: 'solving',
  size: 20,
});

// later…
orb.updateOptions({ paused: true });
orb.destroy();

mountOrb accepts either an existing <canvas> or a container element (it creates the canvas for you). The returned handle exposes controller, canvas, updateOptions, and destroy.

States

Six verbs an agent can be doing, each a distinct animation:

<ThinkingOrb state="working" />    {/* particles on tilted orbits */}
<ThinkingOrb state="searching" />  {/* a scan meridian sweeps a dotted globe */}
<ThinkingOrb state="solving" />    {/* bands scramble, then click back solved */}
<ThinkingOrb state="listening" />  {/* a waveform rolls through the rings */}
<ThinkingOrb state="composing" />  {/* an undulating multi-band sash */}
<ThinkingOrb state="shaping" />    {/* dotted outline: circle → triangle → square */}

Sizes

Two tuned presets — separate designs, not a scale factor. 64 for chat-avatar scale, 20 for inline-text scale. Each carries its own dot count, dot size and speed tuning. Any other numeric size snaps to the nearest preset (≤ 32 → 20, otherwise → 64) while keeping the pixel dimensions you requested.

<ThinkingOrb state="working" size={64} />
<ThinkingOrb state="working" size={20} />

Theme

Strictly monochrome — light ink for dark backgrounds, dark ink for light backgrounds — with the mode picked automatically from the host project:

<ThinkingOrb theme="auto" />   {/* default — detects from the project */}
<ThinkingOrb theme="dark" />   {/* pin: light dots for dark backgrounds */}
<ThinkingOrb theme="light" />  {/* pin: dark dots for light backgrounds */}

auto resolves in three layers and updates live when any of them change:

  1. an ancestor data-theme="dark|light" attribute or dark/light class (the Tailwind / shadcn convention), watched via MutationObserver;
  2. otherwise prefers-color-scheme, subscribed for live OS theme switches;
  3. SSR-safe — the canvas paints only on the client, after the theme has resolved.

Text Shimmer (文字流光)

A lightweight, framework-agnostic "shining text" utility for the status label next to the orb. The styles ship as a standalone CSS file — no framework import required.

/* JS / TSX / any bundler */
import 'thinking-orbs-universal/shimmer.css';
<span class="t-shimmer" data-text="Searching…">Searching…</span>

data-text must equal the visible text — it is used by the ::before layer that renders the moving highlight. Add the .sm class for the inline (size 20) variant:

<span class="t-shimmer sm" data-text="Working…">Working…</span>

The effect reads its tokens from CSS custom properties, so you can tune it from your own theme without re-importing:

| Variable | Description | Default | | -------------------- | --------------------------------- | ----------------- | | --shimmer-dur | Animation duration | 2000ms | | --shimmer-band | Gradient band width | 400% | | --shimmer-ease | Animation easing | linear | | --shimmer-base | Resting text color | light/dark-aware | | --shimmer-highlight| Sweeping highlight color | light/dark-aware |

The base and highlight colors follow the data-theme="light" / dark convention automatically, and the animation is disabled under prefers-reduced-motion: reduce.

CSS custom properties

Drive size, speed, and the two ink colors from CSS instead of props — ideal for theme files and design tokens. Resolved off the element or any ancestor, in real time.

| Variable | Type | Description | Fallback | | -------------------- | ---------- | --------------------------------------------- | --------------------- | | --orb-size | <length> | Rendered size, e.g. 64px, 20px | the size prop | | --orb-speed | <number> | Speed multiplier, e.g. 1.5 | the speed prop | | --orb-color-dark | <color> | Ink color for dark backgrounds | #ffffff | | --orb-color-light | <color> | Ink color for light backgrounds | #000000 |

Resolution priority: explicit props > CSS variables > preset defaults.

thinking-orb {
  --orb-size: 20px;
  --orb-speed: 1.4;
  --orb-color-dark: #e6e6e6;
}

The color variables are fully live: --orb-color-dark paints the dots when the resolved theme is dark, --orb-color-light when it is light. Any CSS color is accepted (#hex, rgb()/rgba()). Each dot renders as that color scaled by its brightness — the brightest dots hold the full hue, deeper dots fall toward black — so you get a custom-tinted orb that keeps its 3D depth. Omit both and the classic grayscale ink is used.

Other props

<ThinkingOrb
  state="solving"
  size={20}
  speed={1.5}          // multiplier on the preset's baked speed
  paused={false}       // freeze on the current frame
  aria-label="Analysing repository…"  // overrides the per-state default
/>

In React, all other <canvas> props (className, style, data-*, …) pass through. In the web component, set aria-label and standard element attributes directly on the tag.

Accessibility & performance

  • role="img" with a sensible per-state aria-label out of the box (Working…, Searching…, …).
  • prefers-reduced-motion: reduce renders a static representative frame — no animation — and still follows the live theme.
  • Every instance pauses automatically when scrolled offscreen (IntersectionObserver) or when the tab is hidden, and resumes in phase — all instances share one clock.
  • Plain 2D canvas arcs only: no ctx.filter, no SVG filters, no WebGL — the same pixels everywhere, cheap on low-end devices. Device-pixel-ratio capped at 2.

Advanced: the core engine

Power users can skip the adapters and talk to the engine directly:

import {
  OrbController,       // bind a canvas, drive the lifecycle yourself
  mountOrb,            // convenience wrapper around OrbController
  ThemeObserver,       // standalone theme resolution + live subscription
  ReducedMotionObserver,
  resolveCSSVars,
  resolvePreset,       // (state, size) → mode + fully-scaled draw options
  STATE_TO_MODE,
  MODE_DRAWS,          // raw per-mode draw functions
  registerThinkingOrbElement,  // pick a custom tag name
} from 'thinking-orbs-universal';

Local demo

npm install
npm run dev      # vite demo playground
npm run build    # library build (dist/)
npm run typecheck

如何修改颜色

开始前先安装本包:npm i thinking-orbs-universal(安装方式见上方 Install)。下面两种颜色都可直接在你的样式里覆盖,无需改源码。

修改文字流光颜色

文字流光(.t-shimmer)的颜色由 CSS 自定义属性控制,直接在你自己的样式里覆盖即可,无需改源码。所有变量都可选,不设就用默认值。

/* 在项目引入流光样式之后覆盖 */
.t-shimmer {
  --shimmer-dur: 1500ms;         /* 扫光动画时长 */
  --shimmer-base: #c7d2fe;       /* 静止时文字的颜色 */
  --shimmer-highlight: #ffffff;  /* 扫过时最亮的高光颜色 */
}
  • 只改文字颜色:覆盖 --shimmer-base。
  • 想调亮高光的扫光颜色:覆盖 --shimmer-highlight。
  • 默认值会自动跟随深浅主题(data-theme="dark|light"),一般无需手动指定。
  • 记得先引入样式文件:import 'thinking-orbs-universal/shimmer.css';,否则 .t-shimmer 类不存在,改动不生效。

修改小球颜色

小球(thinking-orb)的颜色通过两个 CSS 自定义属性设置,按主题分别生效:

thinking-orb {
  --orb-color-dark: #6366f1;   /* 深色背景下小球的颜色,如靛蓝 */
  --orb-color-light: #4f46e5;  /* 浅色背景下小球的颜色 */
}
  • 支持任意 CSS 颜色格式:#hex、rgb() / rgba()。
  • 深色背景用 --orb-color-dark,浅色背景用 --orb-color-light,不设则回退为经典黑白灰。
  • 每个小点按亮度缩放该颜色:最亮的点保持纯色,越远越偏黑,因此着色后仍保留立体感。
  • 也可通过 inline style 针对单个实例覆盖,例如:
    <thinking-orb state="searching" style="--orb-color-dark:#61dafb"></thinking-orb>

License

MIT © Jakub Antalik (original thinking-orbs) and wangbin3162 (thinking-orbs-universal). See LICENSE.