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

nexus-shell

v0.2.11

Published

React component library for building IDE-style application shells: menu bar, activity bar, sidebar, docking layout, terminal, chat and status bar.

Readme

Nexus Shell

React components for building IDE-style application shells — menu bar, activity bar, sidebar, docking layout, terminal, chat pane and status bar.

Documentation and live examples → · Storybook

npm install nexus-shell
import 'nexus-shell/style.css';

React 19 is a peer dependency.

Start with the shell

An application built on this library starts with ShellLayout and grows by registering features into it.

// main.tsx — a complete application
import { createRoot } from 'react-dom/client';
import { Boxes, Files } from 'lucide-react';
import {
  AppTitle,
  ShellLayout,
  chatPanel,
  componentRegistry,
  initializeShell,
  terminalPanel,
  useLayoutStore,
} from 'nexus-shell';
import 'nexus-shell/style.css';

// Views register by id, so the shell never has to import them.
componentRegistry.register('editor', Editor);

initializeShell({
  panels: [
    { id: 'files', label: 'Explorer', icon: Files, component: FileExplorer },
    terminalPanel(),
  ],
  // Chat on the right rail. Move it to `panels` for the left, or register
  // the component as a tab — nothing about it is bound to one edge.
  inspectorPanels: [chatPanel()],
  commands: [
    { id: 'file.save', label: 'File: Save', keybinding: 'Control+s', execute: save },
  ],
  menus: { File: [{ id: 'save', label: 'Save', commandId: 'file.save' }] },
  statusBar: [{ id: 'branch', label: 'main', alignment: 'left' }],
});

useLayoutStore.getState().addTab('editor', 'App.tsx');

createRoot(document.getElementById('root')!).render(
  <ShellLayout title={<AppTitle title="Acme Studio" icon={<Boxes size={16} />} />} />,
);

That is a working IDE: dockable tabs, a command palette on Cmd/Ctrl+Shift+P, a terminal, a chat pane, and a themable frame.

Where things go

The shell has four places a view can live, and nothing is nailed to one of them. Chat and the terminal are ordinary registrations, so they go wherever you put them — including into a dockable tab, alongside your own views.

| Place | How | | :-- | :-- | | Left rail | panels: [...] | | Right rail | inspectorPanels: [...] | | Dockable tab | componentRegistry.register(id, Component) + addTab(id) | | Bottom of the workspace | A tab in the bottom row of your initialLayoutJson |

Anything hosted in a pane or a tab is told so, and drops its own title bar — SidebarPane and the tab strip already draw one. Pass chrome explicitly to force it either way.

Growing an app

You add capability by registering it. None of this changes the layout, which is what lets plugins and distant code contribute to the shell.

| To add | Do this | | :-- | :-- | | A view or tab | componentRegistry.register(id, Component), then useLayoutStore().addTab(id, title) | | An action or hotkey | commandRegistry.registerCommand({ id, label, keybinding, execute }) — appears in the palette automatically | | A menu entry | menuRegistry.registerMenu('File', { id, label, commandId }) | | A sidebar panel | Add it to panels; it becomes an activity bar icon | | An inspector or properties panel | Add it to inspectorPanels; it docks right, with its own rail | | Chat or a terminal, anywhere | chatPanel() / terminalPanel() in panels or inspectorPanels, or register the component as a tab | | A status bar item | useStatusBarStore.getState().addWidget(...) | | A dialog from anywhere | await useModalStore.getState().openConfirm('Sure?') | | An unsaved-changes guard | useLayoutStore.getState().setTabDirty(tabId, true) |

When you need less than the shell

Every component the shell is assembled from is exported on its own — pure and prop-driven, with no global state or registry lookups. This is the escape hatch: reach for it when you're embedding one piece into an app you already have, or building a frame ShellLayout can't express.

import { ActivityBar, SidebarPane, StatusBar, TreeWidget } from 'nexus-shell';

const [active, setActive] = useState<string | null>('files');

<div className="theme-dark flex h-screen flex-col">
  <div className="flex flex-1">
    <ActivityBar
      items={panels}
      activeId={active}
      onSelect={(id) => setActive(id === active ? null : id)}
    />
    {active && (
      <SidebarPane title="Explorer" onClose={() => setActive(null)}>
        <TreeWidget data={nodes} onToggle={toggle} onActivate={open} />
      </SidebarPane>
    )}
    <main className="flex-1">{children}</main>
  </div>
  <StatusBar widgets={[{ id: 'branch', label: 'main', alignment: 'left' }]} />
</div>;

These are controlled, so state stays in your app — a real cost the shell was absorbing for you.

Between the two sit the Connected* variants, each binding one component to its store. ShellLayout is built entirely out of them, so you can rearrange the shell's own frame and keep every registration working.

Components

ShellLayout assembles all of these. Each is also exported on its own.

| Component | What it is | | :-- | :-- | | ActivityBar | Vertical icon rail for switching side panels, either edge | | ChatPane | Chat transcript with slash-command autocomplete; fills any host | | CommandPalette | Filterable, keyboard-navigable command list | | ContextMenu | Floating menu positioned at viewport coordinates | | DataGrid | Sortable, filterable table with optional virtualisation | | MenuBar | Application menu bar with submenus and content slots | | Modal | Alert / confirm / prompt dialog | | QuickSearch | Compact search field with grouped results, for the menu bar | | SearchWidget | Full-height search panel with autocomplete, for the sidebar | | SettingsPanel | Theme picker body for the settings sidebar | | SidebarPane | Titled, closable side panel; side docks it left or right | | StatusBar | Footer with left / center / right item groups | | TerminalPane | Terminal log and input; fills any host | | PaneHostProvider | Tells hosted components a title bar is already drawn | | ThemeSwitcher | Segmented theme control | | TreeWidget | Virtualised tree for any hierarchy, with drag-to-move and a data-driven menu | | GraphCanvas | Infinite pannable, zoomable field for node-and-edge editing | | GraphNode | Positioned, focusable node with edge ports | | GraphEdge | Directed edge with bezier / smoothstep / straight routing | | GraphMiniMap | Scaled graph overview with a viewport indicator | | NodePalette | Drag-to-create and keyboard-to-create node types | | useGraphKeyboard | Spatial keyboard navigation and editing for a graph | | useGraphLayout | Auto layout engines, with a freeform escape for hand placement | | layeredLayout | Layered tree layout in four directions; handles cycles | | gridLayout | Uniform grid, ignoring edges | | PropertyPanel | Inspector for the current selection, with mixed-value editing | | TextFieldStaticField | Nine composable property fields, usable on their own | | FieldShell | The label / description / error frame, for fields of your own | | UserProfile | Avatar and identity widget |

Theming

Four themes ship in the stylesheet: theme-light, theme-dark, theme-gt (Georgia Tech) and theme-tamu (Texas A&M). Colors are CSS custom properties scoped to those classes, so switching themes is a class on an ancestor — no React context, no provider.

Add your own by defining a .theme-yourname block with the same properties.

Development

npm install

npm run site           # documentation site at :5173
npm run storybook      # component workbench at :6006
npm run dev            # showcase app at :5173

npm run typecheck      # library sources
npm test               # vitest — unit and architecture tests
npm run site:test      # renders every site route and demo
npm run build          # library build to dist/
npm run build-storybook
npm run site:build     # documentation site to website/dist/
npm run e2e            # Playwright against the showcase app

npm run typecheck covers src/ only. To include the examples:

npx tsc --noEmit -p tsconfig.examples.json

CI runs all of the above on every push and pull request.

Layout

src/
  components/       Pure, prop-driven components — what the shell is made of
  connected/        Store-bound wrappers used by ShellLayout
  core/             Stores (zustand), registries, and initializeShell
  lib/              Shared utilities
  stories/          MDX documentation pages
examples/
  showcase/         Demo apps built on the library — not published
    dialogue-mapper/
    mockup-reviewer/
    wiki/
    wargame/
    e2e/            Playwright specs for the showcase app
  basic-app/        Minimal integration example
website/
  src/demos/        Live examples — the source shown on the site
  src/content/      The component gallery, as data
  scripts/          Generates the props tables from the library's types

Documentation site

website/ is a Vite app that imports the library from ../src, not from dist. Every example on the site is rendered by the current source, so a demo that no longer compiles against the real API fails the build rather than quietly documenting an API that no longer exists.

Two generation steps keep it honest:

  • Props tables come from website/scripts/generate-api.mjs, which parses the component sources for prop names, types, defaults and JSDoc. Nothing is hand-written.
  • Code samples are extracted from the demo files by #region marker, so the snippet a visitor copies is provably the code that produced the thing they just interacted with.

Adding a component to the site is one entry in website/src/content/components.ts plus a demo file. There is no per-component page to keep in sync.

The site ships no dependencies of its own — routing, syntax highlighting and the copy button are all local, so the docs can't drag the repo's install surface around.

Deployment. .github/workflows/deploy-pages.yml builds the site and Storybook on every push to main and publishes them as one Pages artifact — the site at the root, Storybook under /storybook/. First-time setup needs your credentials: run ./scripts/setup-pages.sh.

The split between components/ and connected/ is the load-bearing one. A component that reads a global store can't be configured by its caller, rendered twice with different data, or documented — its props table is empty because it has no props. Everything in components/ is therefore forbidden from importing core/services, and the tests in components/__tests__ exist to keep it that way.

Anything under examples/ is a consumer of the library, not part of it. It's built and documented alongside the components to prove the primitives are sufficient, but it ships to nobody.

License

MIT