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

@marsaude/devtools-shell

v0.4.0

Published

Self-contained draggable DevTools FAB for QA (admin login, CPF/OTP user login, user generator, session switch). Isolated HttpClient/auth, gated out of production.

Readme

@marsaude/devtools-shell

A content-agnostic, draggable floating-action-button shell for dev-only tools.

It ships only the invólucro: a FAB you can drag anywhere on screen (mouse + touch, position persisted across reloads, clamped to the viewport) that opens a container. The panel content is yours — plugged in from the outside. The shell has zero knowledge of auth, APIs, or any business domain.

  • Angular standalone + Signals + @if/@for control flow. No NgModules.
  • Drag/animations ported verbatim from the DevTools FAB prototype — plain Pointer Events, no external drag library.
  • Auto-mounts itself (createComponent + ApplicationRef.attachView from an APP_BOOTSTRAP_LISTENER) — no tag to place in a template.

Shell behaviour (all domain-free): drag + snap-to-edge + persisted position; idle → collapse into a thin edge grip (tap to restore); tap → radial speed-dial of the registered actions (or open directly when there's a single action); pick an action → its content renders in a side-drawer / bottom-sheet.

Toast: inject DevtoolsToastService anywhere and call show('…') to flash a message inside the shell layer (no Material dependency):

import { DevtoolsToastService } from '@marsaude/devtools-shell';
private readonly toast = inject(DevtoolsToastService);
this.toast.show('Usuário gerado');

Install

npm i @marsaude/devtools-shell
# peers (already present in this workspace):
npm i @angular/core @angular/common

Material Symbols are used for glyphs. Load them once in the host app if you want the icons to render:

<link
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined"
  rel="stylesheet"
/>

Mount the shell

Add provideDevtools() to your application providers.

Standalone bootstrap

import { bootstrapApplication } from '@angular/platform-browser';
import { provideDevtools } from '@marsaude/devtools-shell';
import { environment } from './environments/environment';

bootstrapApplication(AppComponent, {
  providers: [
    provideDevtools({
      enabled: !environment.production, // Layer-1 gate (see below)
      title: 'DevTools',
      actions: [],
    }),
  ],
});

NgModule app (this repo)

provideDevtools() returns EnvironmentProviders, valid in @NgModule.providers:

@NgModule({
  // ...
  providers: [
    provideDevtools({ enabled: !environment.production }),
  ],
})
export class AppModule {}

That's all — a draggable FAB appears, and clicking it opens an (empty) panel.

Pass the interface later (the extension point)

The panel content is supplied as actions. Each action is a standalone component (or a TemplateRef) — the shell just renders it.

import { provideDevtools } from '@marsaude/devtools-shell';
import { MyUserGeneratorPanel } from './devtools/user-generator-panel';

provideDevtools({
  enabled: !environment.production,
  actions: [
    { id: 'gen',   label: 'Gerador', icon: 'groups',  content: MyUserGeneratorPanel },
    { id: 'login', label: 'Logar',   icon: 'login',   content: MyLoginPanel },
  ],
});
  • One action → it opens directly in the container.
  • Multiple actions → the shell renders a tab switcher in the panel header.
  • Your panel component is rendered with NgComponentOutlet; it can inject its own services normally.

Two doors for content

  1. Action registry (primary). Shown above. This is the recommended path because the shell auto-mounts onto document.body — it has no place in your template, so there is nowhere to project into.

  2. Content projection (secondary). Only when you place the component yourself instead of using provideDevtools auto-mount:

    <devtools-shell>
      <my-panel />
    </devtools-shell>

    <ng-content> is rendered when no actions are registered.

Why the registry is primary: auto-mounting via createComponent (the pattern reused from the original DevTools boot flow) means the shell lives outside any consumer template. <ng-content> requires the consumer to host the tag, which contradicts auto-mount. The token-based registry decouples where the shell lives from who supplies the content.

Re-skinning

The shell ships the dark theme from the original mockup, exposed as CSS custom properties on the host element. Override them anywhere:

[data-devtools-shell-host] devtools-shell {
  --dts-bg: #14151b;
  --dts-accent: #ffc454;
  --dts-fg: #eceef3;
  --dts-font: system-ui, sans-serif;
}

Production gating

The shell must never reach a production bundle. Two layers:

Layer 1 — runtime flag (always on)

Pass enabled: !environment.production. When false, provideDevtools() returns no providers: nothing mounts, no actions register.

Layer 2 — build-time elimination (recommended)

Layer 1 still leaves the shell imported. To drop it from the bundle entirely, isolate the wiring in one file and swap it via fileReplacements.

src/app/devtools/devtools.providers.ts (dev):

import { EnvironmentProviders } from '@angular/core';
import { provideDevtools } from '@marsaude/devtools-shell';
import { UserGeneratorPanel } from './user-generator-panel';

export function devtoolsProviders(): EnvironmentProviders[] {
  return [
    provideDevtools({
      enabled: true,
      actions: [{ id: 'gen', label: 'Gerador', icon: 'groups', content: UserGeneratorPanel }],
    }),
  ];
}

src/app/devtools/devtools.providers.prod.ts (prod no-op — imports nothing):

export function devtoolsProviders() {
  return [];
}

angular.json (production configuration):

"fileReplacements": [
  { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" },
  { "replace": "src/app/devtools/devtools.providers.ts", "with": "src/app/devtools/devtools.providers.prod.ts" }
]

Use it in bootstrap:

import { devtoolsProviders } from './app/devtools/devtools.providers';
// providers: [ ...devtoolsProviders() ]

Because the prod file imports neither @marsaude/devtools-shell nor your panel components, the bundler tree-shakes the whole shell out of the production build.

Verification checklist

  • [ ] provideDevtools({ enabled: !environment.production }) — Layer 1 in place.
  • [ ] Wiring isolated in devtools.providers.ts with a .prod.ts no-op twin.
  • [ ] fileReplacements entry for the providers file added to the production configuration in angular.json.
  • [ ] environment.prod.ts actually has production: true for the real prod env.
  • [ ] Run a prod build and confirm the shell is gone: ng build --configuration production then grep -r "devtools-shell\|data-devtools-shell-host" dist/ returns nothing.
  • [ ] Load the prod bundle: no FAB on screen, no [data-devtools-shell-host] element in the DOM.

Build & publish (public npm — scope @marsaude)

Published as a public scoped package on npmjs (free). You must be logged in as a user that owns the @marsaude scope.

# one-time: authenticate against npmjs
npm login            # npm whoami → marsaude

# build + publish in one step (from the workspace root)
npm run publish:lib
#  → runs `ng build devtools-shell` then
#    `npm publish ./dist/devtools-shell --access public`

Manual equivalent:

npm run build:lib                                   # → dist/devtools-shell
npm publish ./dist/devtools-shell --access public   # note the ./ prefix

Bump the version in projects/devtools-shell/package.json before each release (0.1.00.1.1 …). peerDependencies: @angular/core, @angular/common (^21).

--access public is required for the first publish of a scoped package on the free npm plan (private/restricted needs a paid npm plan → E402).