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

@demystify/tools-ui

v0.4.2

Published

Design tokens + the shared Demystify Tools floating panel (search, filter pills, per-host recents, per-app pinned grid). Mode A: first-party React component, no iframe.

Readme

@demystify/tools-ui

Design tokens and the shared Demystify Tools floating panel — search, filter pills, per-host "recently used" memory, and a per-app pinned grid. Mode A: a first-party React component you embed via npm (no iframe), themed by your host tokens.

pnpm add @demystify/tools-ui
# peers: react >=18, react-dom >=18

Quick start (e.g. Finokit)

"use client";
import { ToolsPanel, type PanelTool } from "@demystify/tools-ui";
import configs from "@demystify/tools-ui/widget-configs.json"; // per-app pins (Finokit, ASHR, …)
import "@demystify/tools-ui/panel.css";                        // required
import "@demystify/tools-ui/tokens.css";                       // optional Demystify theme

// Your tool list — from @demystify/tools-registry, or fetch the hosted search index once:
//   const tools = (await fetch("https://tools.demystifysystem.com/search/index.json")).json()
const tools: PanelTool[] = /* [{ id, name, desc?, category?, suite?, keywords?, url? }, …] */ [];

export function FinokitToolsButton() {
  return (
    <ToolsPanel
      tools={tools}
      product="finocket"          // resolves pinned_top + placement from widget-configs.json
      configs={configs}
      context={{ module: "invoicing", screen: "new-invoice" }} // optional: pre-filters relevant tools
      onOpenTool={(tool) => openInYourSlideOver(tool)}          // omit to open the hosted page in a new tab
    />
  );
}

That renders a draggable brand puck (and Cmd/Ctrl+K) — drag it anywhere; it remembers its spot per host and snaps to the nearest side. Opening it shows Finokit's 8 pinned tools, a search box over the whole catalog, filter pills (Pinned · Most used · Recent · your context · top categories), and per-host recents + usage. No iframe, no network on mount, no tracking.

The puck

  • Draggable & repositionable (iOS-style): drag to move, release to snap to the nearest edge, position saved in localStorage per host. Set draggable={false} to pin it to a corner.
  • Branded icon: defaults to the hosted Demystify mark, with an inline SVG fallback if the image can't load (so it works offline). Override with logoUrl="…".
  • Local usage tracking: every open increments a per-tool count in localStorage (namespaced per product) and powers the Most used pill. Read it yourself with readUsage(namespace).

What you control

| Prop | Purpose | |---|---| | tools | the catalog to search/pin over (host supplies it) | | product + configs | resolve pinned grid + placement from widget-configs.json | | config / pinnedTop | supply a config object or pin ids directly instead | | context | { module, screen } — adds a relevant filter pill and biases results | | ai | in-panel Mysty chat: { enabled, send? , endpoint?, token?, model?, greeting?, suggestions? } (see below) | | onOpenTool(tool) | the key seam — open the tool in your own slide-over/embed/route; omit → new tab | | baseUrl | origin for tool URLs + footer links (default tools.demystifysystem.com) | | draggable · logoUrl | drag+remember the puck (default on) · puck icon (default Demystify mark) | | placement · trigger · hotkey | bottom-right … · fab | cmdk | fab+cmdk | none · Cmd/Ctrl+K | | onEvent | aggregate-only telemetry (open/search/open_tool …), never PII | | labels | i18n string overrides |

Ask Mysty — the in-panel AI chat

Pass an ai config to add an Ask Mysty tab next to Tools. It's provider-agnostic — the host decides how it's authorized, so keys/tokens never live in this component. The tab only appears when ai.enabled and a way to send is wired.

BYOK (the user's own key — Miatz students, "add a key", or the tools app's /api/ai proxy) — give a send:

<ToolsPanel
  tools={tools} product="finocket" configs={configs}
  ai={{
    enabled: true,
    name: "Mysty",
    greeting: "Hi, I'm Mysty. Ask me about GST, invoices, anything.",
    suggestions: ["Explain GST input credit", "Draft a payment reminder"],
    send: async (messages, { signal }) => {
      const key = getUserKey();                    // from your local BYOK store
      const res = await fetch("https://api.provider.com/v1/chat", {
        method: "POST", signal,
        headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
        body: JSON.stringify({ messages }),
      });
      return (await res.json()).choices[0].message.content;
    },
  }}
/>

Host-token (Finokit / ashr.work entitlement) — give an endpoint + the platform token; the built-in adapter POSTs { messages, model } with Authorization: Bearer <token>:

ai={{ enabled: true, endpoint: "https://mysty.demystipai.com/chat", token: sessionToken, model: "mysty-1" }}

The adapter reads the reply from {reply} / {content} / {message} / OpenAI-style choices[0].message.content. Chat history is kept locally per host (localStorage, cleared with the Clear button). Nothing is sent until the user hits Send.

Not wired yet? Leave ai off (default) and the panel is tools-only. Set ai.enabled without a send/endpoint and the tab stays hidden until you connect one — a safe seam.

BYOK AI toolkit — one shared, self-updating catalog

The package ships the whole "bring your own key" stack so every app is consistent and models update in one place:

  • AI_PROVIDERS — a typed catalog of ~16 providers (free: Groq · Google Gemini · OpenRouter · Cerebras · NVIDIA · Mistral · Cohere · Together · self-hosted; paid: OpenAI · Anthropic · xAI · DeepSeek · Perplexity · Fireworks) with base URLs, key-origin links, and current models. Also served live at https://tools.demystifysystem.com/ai-models.json (CORS) so new models roll out centrally without a release — fetch it, fall back to the bundle.
  • <KeyManager /> — a drop-in "Bring your own key" settings panel. Renders on your settings page; the user picks a provider (free ones grouped first), pastes a key, picks a model. Saved to this origin's localStorage only.
  • key storereadKeys / saveKey / removeKey / activeKey / hasAnyKey / setSelectedProvider.
  • makeBrowserSend() — an AiConfig.send backed by that store; the browser calls the user's chosen provider directly (OpenAI-compatible + Anthropic). Their key never touches a Demystify server.

Wire the chat to BYOK with one flag — no send needed:

import { ToolsPanel, KeyManager } from "@demystify/tools-ui";

// settings page:
<KeyManager />

// anywhere (the puck):
<ToolsPanel tools={tools} product="finocket" configs={configs}
  ai={{ enabled: true, byok: true }} />   // uses the local key; if none, the chat says "add a key in Settings"

Per-module AI: give each module its own recentsKey/namespace and (if you want different models per screen) pass a per-module ai.send that reads that module's chosen key. The default byok:true shares one key across the app.

Free to start: tell users to grab a free key from Groq or Google AI Studio (instant, no card) — KeyManager links straight to each provider's key page.

CORS note: Groq, Google, OpenRouter, Cerebras, xAI, Anthropic and self-hosted allow direct browser calls. A few providers block browser CORS — route those through a proxy (ai.endpoint) or pick a browser-friendly one.

Theming

The panel reads a --dmt-* token bridge, falling back to Demystify --color-* tokens (if you import tokens.css), then sane light/dark defaults. Map your host tokens once to restyle everything:

.dmt-root { --dmt-accent: var(--brand); --dmt-surface: var(--card); --dmt-radius: 10px; }

Dark mode follows prefers-color-scheme; pin it with [data-dmt-theme="light"|"dark"] on any ancestor.

Recents

Stored locally per host (localStorage, namespaced by product/recentsKey). Each app keeps its own recents — partitioned by design, not shared across products.

Also exported

rank / score (the pure ranker), readRecents / pushRecent, readUsage / recordUsage / mostUsedIds, readPosition / writePosition / snapToEdge / clampToViewport, resolveConfig / resolvePinned / deriveFilters / toolUrl, the AI toolkit (AI_PROVIDERS, KeyManager, makeBrowserSend, sendWithKey, key-store fns), and all types — so you can build a custom surface on the same core. Data-only import: @demystify/tools-ui/providers.

Notes

  • widget-configs.json is generated at build from the repo SSOT registry/widget-configs.json.
  • This ships the launcher. Running a tool in-process (engines rendered inside the panel) is the next layer; the onOpenTool seam is where it plugs in — until then, open the hosted tool or an embed.
  • License: Apache-2.0.