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

@xtatistix/tour-agent-demo

v0.1.1

Published

Static interactive demo player for TourAgent landing pages

Readme

@xtatistix/tour-agent-demo

Framework-agnostic DOM capture and static demo package for landing pages, onboarding flows, and step-by-step product walkthroughs.

This package now covers two related jobs:

  • capture live DOM states step by step and export them as static demo assets
  • render static demo snapshots in an isolated ShadowRoot or iframe
  • load exported capture sets back from manifest.json and play them directly

It stays browser-native and does not depend on React, Vue, or Angular.

Install

npm install @xtatistix/tour-agent-demo

Capture Flow Example

import {
  createCaptureSession,
  mountCaptureWidget,
  exportCaptureSession,
} from '@xtatistix/tour-agent-demo';

const session = createCaptureSession({
  projectName: 'analysis-demo',
  scope: document,
  sanitize: {
    textInputMode: 'preserve',
    sensitiveValueMode: 'mask',
  },
});

const widget = mountCaptureWidget({
  session,
  position: 'bottom-left',
});

window.demoCapture = {
  capture: (label) => session.captureStep({ label }),
  export: () => exportCaptureSession(session),
  destroy: () => widget.destroy(),
};

The floating widget adds a tiny isolated panel with:

  • Capture Step
  • Export Session
  • Clear Steps
  • optional label input

Each click stores a sanitized snapshot in memory, so the user can move through a wizard or modal flow and capture each visible state in order.

Output Format

Exported sessions produce a small manifest plus one HTML file per step:

analysis-demo/
  manifest.json
  step-001.html
  step-002.html
  step-003.html

Example manifest:

{
  "version": 1,
  "captureSetId": "analysis-demo-lt9v5o-a1b2c3",
  "projectName": "analysis-demo",
  "createdAt": "2026-04-04T09:00:00.000Z",
  "steps": [
    {
      "id": "step-001",
      "order": 1,
      "file": "step-001.html",
      "label": "Initial screen",
      "createdAt": "2026-04-04T09:00:02.000Z",
      "sourceUrl": "https://app.example.com/wizard",
      "scope": { "type": "document" },
      "title": "Wizard"
    }
  ]
}

Public API

createCaptureSession(config)

Creates an in-memory capture set.

const session = createCaptureSession({
  projectName: 'analysis-demo',
  scope: document,
  sanitize: {
    textInputMode: 'preserve',
    sensitiveValueMode: 'mask',
  },
});

Session methods:

  • session.captureStep({ label?, scope?, sanitize? })
  • session.getSteps()
  • session.getStepCount()
  • session.getManifest()
  • session.clear()
  • session.export()

captureDOMSnapshot(options)

Captures a single sanitized snapshot without creating a session.

const snapshot = captureDOMSnapshot({
  scope: '#wizard-root',
});

exportCaptureSession(session, options)

Uses progressive enhancement for saving:

  • File System Access API when available
  • browser downloads when not available

By default, supported browsers ask the user to pick a directory and then create a subfolder named after the project.

await exportCaptureSession(session);

sanitizeSnapshotHTML(rootOrHtml, options)

Creates a static, safer HTML document from a Document, Element, selector-backed scope, or raw HTML string.

Default sanitization includes:

  • remove script, iframe, noscript, object, embed
  • remove inline event handlers like onclick
  • strip common third-party widget and consent containers
  • clear hidden inputs
  • clear passwords and file inputs
  • mask or clear sensitive token/auth-like values
  • neutralize dangerous URLs such as javascript:
  • inject a <base> tag so exported demos resolve relative assets more reliably later

Scoped Capture

The first-class path is full-page capture via document, but the API is ready for narrower scopes:

const session = createCaptureSession({
  projectName: 'pricing-flow',
  scope: '#pricing-wizard',
});

When the scope is an element instead of the full document, the export still includes the current page <head> so stylesheets and CSS remain available for later playback.

Static Playback API

The earlier player API remains available for rendering captured HTML snapshots in an isolated surface:

import { createDemoPlayer } from '@xtatistix/tour-agent-demo';

const player = createDemoPlayer({
  container: '#interactive-demo',
  isolation: 'iframe',
  initialStep: 1,
  snapshots: {
    '1': '<div><button data-tour-id="cta">Start</button></div>',
    '2': '<div><button data-tour-id="review">Review</button></div>',
  },
});

If you already exported a capture set, you no longer need to hand-build the snapshots map yourself.

import { createCapturePlayback } from '@xtatistix/tour-agent-demo';

const playback = await createCapturePlayback({
  container: '#demo-root',
  manifestUrl: '/demo/analysis-demo/manifest.json',
  isolation: 'iframe',
});

document.querySelector('[data-next]').addEventListener('click', () => {
  playback.next();
});

document.querySelector('[data-prev]').addEventListener('click', () => {
  playback.prev();
});

This path is the recommended one for exported sessions because iframe playback now preserves full exported documents, including <head> content and linked CSS.

Browser Notes

  • Directory export depends on the File System Access API, which is not available in every browser.
  • Download fallback saves each file separately with a project-name prefix.
  • Open shadow roots from third-party components are not serialized deeply in this version.
  • Runtime canvas contents are not rasterized in this version.
  • Relative assets are helped by the injected <base> tag, but cross-origin or authenticated assets can still fail during playback.
  • If the original page relied on runtime-injected CSS that is not reachable anymore, playback can still look incomplete.

Future Playback Readiness

The capture/export format is designed to plug into a later iframe playback layer:

  • one HTML file per captured step
  • a small manifest for ordering and labels
  • per-step source metadata
  • sanitized static HTML that can be fed into iframe.srcdoc