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

itk-corteo

v1.0.0

Published

A lightweight “guided demo” layer for React apps:

Readme

ITK-Corteo (Demo Mode)

A lightweight “guided demo” layer for React apps:

  • Record real user interactions (click/input/select/accordion + URL changes)
  • Play them back with a spotlight, coachmarks, and draggable controls
  • Load demo configs from static JSON via a simple manifest

In this repo the package name is itk-corteo.

Installation

bun add itk-corteo

Peer deps: react, react-dom, react-router-dom, theme-ui.

Quick Start

  1. Serve your demo configs (a manifest.json + one or more *.json configs).

Example structure (matches this repo’s playground):

public/
  demos/
    manifest.json
    test-form-demo.json
  1. Wrap your app with DemoMode.
import { DemoMode } from 'itk-corteo';

export function App() {
  return (
    <DemoMode enabled={true} configSrc="/demos">
      {/* your app */}
    </DemoMode>
  );
}
  1. Open the command palette with Ctrl/Cmd + Shift + D, then select a demo.

Components

DemoMode

The top-level provider + UI shell (command palette + player overlay + recorder UI).

export interface DemoModeProps {
  configSrc: string;
  enabled: boolean;
  children: React.ReactNode;
}
  • enabled=false makes DemoMode a no-op (renders children only).
  • configSrc is the base URL for demo assets; DemoMode fetches:
    • ${configSrc}/manifest.json
    • each ${configSrc}/{file} listed in the manifest

DemoRecorder

Optional, but recommended in development: adds a “recording HUD” and save dialog.

import { DemoMode, DemoRecorder } from 'itk-corteo';

<DemoMode enabled={true} configSrc="/demos">
  <DemoRecorder />
  {/* your app */}
</DemoMode>;

Recording Demos

  • Open the command palette (Ctrl/Cmd + Shift + D) and click Start Recording.
  • Interactions are captured globally (document-level capture listeners):
    • click
    • input (debounced)
    • select (native <select> and zenith-style custom options)
    • accordion (elements with aria-expanded or data-*accordion* attributes)
    • URL changes via History API + popstate

When you press Save, the recorder downloads a *.json DemoConfig file.

Playing Demos

During playback you’ll see:

  • A spotlight cutout around the current target element
  • An optional coachmark (shown only when the step has title or description)
  • Draggable controls (they reset to top-center when a new demo starts)

Keyboard shortcuts during playback:

  • ArrowRight: next step
  • ArrowLeft: previous step
  • Space: play/pause
  • Escape: stop

Loading configs (Manifest)

configSrc is expected to contain a manifest.json:

{
  "demos": [
    { "id": "test-form-demo", "file": "test-form-demo.json" }
  ]
}

Only configs that pass validateDemoConfig() are included.

Configuration Schema

DemoConfig

export interface DemoConfig {
  id: string;
  name: string;
  version: string;
  steps: DemoStep[];
  description?: string;
  createdAt?: string;
  updatedAt?: string;
}

DemoStep

export type StepAction =
  | 'click'
  | 'input'
  | 'select'
  | 'accordion'
  | 'navigate'
  | 'wait'
  | 'custom';

export interface DemoStep {
  id: string;
  title?: string;
  description?: string;
  target: TargetConfig;
  action: StepAction;
  value?: string;
  url?: string;
  expectedUrlAfter?: string;
  captures?: CapturedRequest[];
  apiMode?: ApiMode;
  onNext?: () => Promise<void>;
  onBack?: () => Promise<void>;
  sensitive?: boolean;
}

Notes:

  • navigate is best-effort and uses history.pushState.
  • wait treats value as milliseconds.
  • custom runs onNext when executing the step.

Selector strategies

A target is an ordered list of strategies; higher priority strategies are tried first:

  1. testid
  2. label
  3. formField
  4. contextual
  5. nthMatch
  6. css

Examples:

{ "type": "testid", "value": "submit" }
{ "type": "label", "value": "Company Name" }
{ "type": "contextual", "parent": "[data-testid='person-0']", "selector": "input" }
{ "type": "css", "selector": "button[type='submit']" }

API Interception

The repo includes a small fetch interceptor core (src/core/apiInterceptor.core.ts) supporting:

  • passthrough: call real fetch (optionally capture requests)
  • replay: return a mocked Response for captured { method, url } matches
  • custom: run a custom hook and then call real fetch

Captured requests are represented as:

export interface CapturedRequest {
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  url: string;
  requestBody?: unknown;
  responseBody?: unknown;
  responseStatus?: number;
  timestamp?: number;
}

Multi-App Demos (Module Federation)

Demo/recording state is persisted to sessionStorage under keys prefixed with itk-corteo:. If multiple micro-frontends share the same browser session and bundle this library, they can resume playback/recording state after navigation.

Local Development

bun install
bun run dev
  • bun run dev starts the playground Vite server on http://localhost:3000
  • In this repo, the playground mounts:
    • <DemoMode configSrc="/demos" enabled={true}>
    • demo assets live in playground/demos/

Troubleshooting

  • No demos show up: ensure ${configSrc}/manifest.json is reachable and lists the right files.
  • A demo config is ignored: it must pass validateDemoConfig() (id/name/version + at least 1 step + valid target strategies).
  • Keyboard shortcuts don’t work: enabled must be true, and focus must not be inside an input/textarea.

Zenith-UI Targeting Guide

Context: Most teams don't use testIds when implementing Zenith components. This section explains how the recorder targets elements when testIds are missing and how to make your Zenith components targetable.

The Core Problem

Zenith components support testIds, but teams often don't use them:

// What teams write (no testId):
<TextInput labelText="Email" value={email} />

// What you should write (with testId):
<TextInput labelText="Email" value={email} testId="email-input" />

Result: The recorder uses fallback strategies that work with Zenith's architecture.

How Targeting Works

The selector engine tries 6 strategies in priority order until one succeeds:

1. testid (Best - Recommended)

  • Looks for data-testid attribute
  • Example: data-testid="email-input"
  • Most reliable - always add testId props when possible
  • Also checks data-test as fallback
  • Supports prefix matching for dynamic IDs

2. label (Primary Fallback)

  • Looks for visible text labels associated with the element
  • Works with Zenith's <Text> component (not just HTML <label> tags)
  • Example: <Text>Email</Text> next to <TextInput> → targets by "Email"
  • This is the primary successful strategy for Zenith components without testIds

3. formField (Form-Aware)

  • Uses form structure + name attribute + label text
  • Example: form 'Personal Details' → field 'email'
  • Works with Zenith form-like containers (not just <form> tags)
  • Secondary fallback when labels aren't clear

4. contextual (Nearby Text)

  • Uses nearby visible text as context
  • Example: "near text 'Contact Information'"
  • Works well with Zenith (lots of Text components)

5. nthMatch (Position-Based)

  • Uses element position: "3rd button on the page"
  • Fragile - breaks if layout changes
  • Used as last resort only

6. css (Raw CSS)

  • Raw CSS selector
  • Not recommended with Zenith - Theme-UI generates unstable class hashes
  • Absolute last resort

Best Practices for Zenith Components

✅ Recommended: Always Add TestIds

// Buttons
<Button testId="submit-btn">Submit</Button>

// Form inputs
<TextInput testId="email-input" labelText="Email" />
<SelectInput testId="state-select" labelText="State" options={...} />

// Accordions
<Accordion testId="person-1-accordion" title="Person 1">

// Any interactive element
<Link testId="next-page-link" href="/next">

✅ Good: Use Clear, Unique Labels

If testIds aren't possible:

  • Use clear, unique label text
  • Keep form structure semantic
  • Avoid duplicate labels in the same context
  • The recorder will automatically use the label strategy
// These will work via label detection:
<TextInput labelText="Email Address" /> // ✅ Clear label
<TextInput labelText="Company Name" />  // ✅ Unique

// Avoid:
<TextInput labelText="Name" />          // ❌ Ambiguous
<TextInput labelText="Name" />          // ❌ Duplicate

⚠️ Avoid: Position-Based Selectors

Don't rely on position - these break easily:

// This generates fragile "3rd button" selector
<div>
  <Button>Cancel</Button>
  <Button>Back</Button>
  <Button>Submit</Button> {/* Position-based, fragile! */}
</div>

// Better: add testId
<Button testId="submit-btn">Submit</Button>

Recording Overlay (Inspector Tool)

During recording, use the Recording Overlay to see generated selectors in real-time:

  1. Start recording (Ctrl+Shift+D → Start Recording)
  2. Toggle overlay (Ctrl+Shift+O)
  3. Click "Inspect" button
  4. Hover over any element (blue highlight appears)
  5. Click element to see all generated selectors
  6. Review color-coded strategies:
    • 🟢 Green = testId (most reliable)
    • 🟡 Yellow = label/formField (semantic, stable)
    • 🔴 Red = nthMatch/css (fragile)
  7. Copy any selector to clipboard

Troubleshooting Selectors

"Element not found" during playback:

  • Check if element has testId → add one
  • Check if label text is unique → make it more specific
  • Use Recording Overlay to see which strategy was used
  • Avoid relying on position-based selectors

"Wrong element selected":

  • Duplicate labels or ambiguous text
  • Add testIds to disambiguate
  • Use more specific label text

"Selector breaks after code changes":

  • Position-based selector was used (nthMatch)
  • Add testId to the element
  • Or ensure label text stays consistent