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

@adpal/app-switcher

v1.0.5

Published

Shared app switcher and multi-account components for AdPal products

Readme

@adpal/app-switcher

Shared app switcher and multi-account components for AdPal products. Provides a Google Workspace-style header with app grid and multi-account support.

Features

  • AppSwitcher - Google Workspace-style app grid for switching between AdPal products
  • AccountDropdown - Multi-account support with account switching
  • AccountAvatar - Reusable avatar component with loading states and fallbacks
  • Header - Combined header component with app switcher and account menu
  • useLinkedAccounts - Hook for cross-subdomain account persistence
  • Custom SVG Icons - Gradient icons with glow effects (BizIntel, Data, Voice, MCP)

Installation

# From GitHub (recommended for private repos)
npm install github:Data-Subsystems/adpal-app-switcher

# Or from npm (if published)
npm install @adpal/app-switcher

Peer Dependencies

  • react >= 18.0.0
  • react-dom >= 18.0.0
  • next >= 14.0.0
  • next-auth >= 4.0.0

Quick Start

1. Install the package

npm install github:Data-Subsystems/adpal-app-switcher

2. Create Header component

// components/layout/Header.tsx
'use client';

import { useSession, signOut } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { Header as SharedHeader, themes } from '@adpal/app-switcher';

export function Header() {
  const router = useRouter();
  const { data: session, status } = useSession();

  const handleSwitchAccount = async (account: { email: string }) => {
    await signOut({ redirect: false });
    const response = await fetch('/api/auth/switch', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: account.email }),
    });
    if (response.ok) {
      const { redirectUrl } = await response.json();
      window.location.href = redirectUrl;
    }
  };

  const handleAddAccount = async () => {
    const response = await fetch('/api/auth/switch', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ addNew: true }),
    });
    if (response.ok) {
      const { redirectUrl } = await response.json();
      window.location.href = redirectUrl;
    }
  };

  return (
    <SharedHeader
      user={session?.user ?? null}
      isLoading={status === 'loading'}
      theme={themes.blue}  // or themes.cyan
      heightClass="h-14"
      appSwitcher={{
        currentAppId: 'bizintel',  // or 'data'
      }}
      accountDropdown={{
        onSwitchAccount: handleSwitchAccount,
        onAddAccount: handleAddAccount,
        onSignOut: () => signOut({ callbackUrl: '/auth/signin' }),
      }}
    />
  );
}

3. Create API routes

// app/api/auth/switch/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { email, addNew } = body;

    const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000';
    const params = new URLSearchParams({ callbackUrl: `${baseUrl}/` });

    if (email && !addNew) {
      params.set('login_hint', email);
    }

    const redirectUrl = `${baseUrl}/api/auth/signin/google?${params.toString()}`;
    return NextResponse.json({ redirectUrl });
  } catch {
    return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
  }
}

4. Update auth config

// lib/auth-options.ts
GoogleProvider({
  clientId: process.env.GOOGLE_CLIENT_ID!,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  authorization: {
    params: {
      prompt: 'select_account',  // Enable account picker
    },
  },
}),

5. Update middleware

// middleware.ts
const publicPaths = [
  // ... existing paths
  '/api/auth/accounts',
  '/api/auth/switch',
];

Available Apps

| ID | Name | URL | Icon Color | |----|------|-----|------------| | bizintel | Business Intelligence | https://b.adpal.com | Gold | | data | Data Processing | https://d.adpal.com | Cyan | | voice | Voice Assistant | https://voice.adpal.com | Green | | mcp-demo | MCP Demo | https://mcp-demo.adpal.com | Purple |

Themes

import { themes } from '@adpal/app-switcher';

// Available themes
themes.blue    // Default - blue accent
themes.cyan    // Cyan accent (#00BCD4)
themes.emerald // Green accent

// Or create custom
const customTheme = {
  accent: '#FF5722',
  ringColor: 'ring-orange-200',
  currentBg: 'bg-orange-50',
};

Components API

Header

<Header
  user={session?.user ?? null}
  isLoading={status === 'loading'}
  theme={themes.blue}
  heightClass="h-14"          // Header height (h-12, h-14, h-16)
  sidebarOffset="left-64"     // Left offset for sidebar
  appSwitcher={{
    currentAppId: 'bizintel',
    showHeader: true,
    showFooter: true,
    columns: 3,
  }}
  accountDropdown={{
    onSwitchAccount: (account) => {},
    onAddAccount: () => {},
    onSignOut: () => {},
    showManageAccount: true,
  }}
/>

AppSwitcher

<AppSwitcher
  currentAppId="bizintel"     // Highlight current app
  apps={ADPAL_APPS}           // Custom apps list
  showInternalApps={true}     // Show internal-only apps
  iconSize={48}               // Icon size in pixels
  columns={3}                 // Grid columns (2 or 3)
  showHeader={true}           // Show "Adpal Apps" header
  showFooter={true}           // Show "More from Adpal" footer
  onAppClick={(app) => {}}    // Custom click handler
/>

AccountDropdown

<AccountDropdown
  user={{ id, email, name, image }}
  isLoading={false}
  onSwitchAccount={(account) => {}}
  onAddAccount={() => {}}
  onSignOut={() => {}}
  showManageAccount={true}
  manageAccountUrl="https://myaccount.google.com"
/>

useLinkedAccounts Hook

const {
  accounts,           // LinkedAccount[]
  isLoaded,          // boolean
  addAccount,        // (account) => void
  removeAccount,     // (accountId) => void
  updateLastUsed,    // (accountId) => void
  clearAllAccounts,  // () => void
} = useLinkedAccounts({
  cookieDomain: '.adpal.com',  // Optional
  maxAccounts: 5,              // Optional
});

Cross-Subdomain Account Persistence

Linked accounts are stored in a cookie with domain .adpal.com, enabling account persistence across all AdPal subdomains:

  • Cookie name: adpal-linked-accounts
  • Max accounts: 5
  • Expiry: 30 days
  • Fallback: localStorage (if cookie exceeds 4KB)

Projects Using This Package

| Project | Subdomain | Theme | Current App ID | |---------|-----------|-------|----------------| | adpal-ai-bizintel | b.adpal.com | themes.blue | bizintel | | adpal-ai-data | d.adpal.com | themes.cyan | data |

Development

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run dev

# Type check
npm run typecheck

Updating the Package

After making changes:

# Build and commit
npm run build
git add -A
git commit -m "description"
git push

# Update in consuming projects
cd ../adpal-ai-data
npm update @adpal/app-switcher

cd ../adpal-ai-bizintel
npm update @adpal/app-switcher

Repository

https://github.com/Data-Subsystems/adpal-app-switcher

License

MIT