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

@nstudio/nativescript-driver

v1.0.1

Published

Use the driver.js walkthrough/coachmark library as-is in NativeScript (dominative + MasonKit DOM), rendered natively on iOS and Android.

Readme

@nstudio/nativescript-driver

Use the popular web walkthrough library driver.js as-is in a NativeScript app. The driver.js package runs unmodified; this plugin provides the small NativeScript DOM compatibility layer that lets it render natively on iOS and Android on top of dominative + @triniwiz/nativescript-masonkit.

How it works

driver.js draws a full-screen <svg> with a single <path fill-rule="evenodd"> cutout (animated each frame via requestAnimationFrame) and a positioned popover. None of that runs on a logical DOM out of the box, so the plugin fills the gaps:

  • getBoundingClientRectview.getLocationInWindow() + getActualSize() (DIPs).
  • window.innerWidth/innerHeightScreen.mainScreen.*DIPs; requestAnimationFrame is the native global; getComputedStyle is a minimal shim.
  • classList / contains / closest / matches / querySelector(All) / getElementById / innerHTML are patched onto the element/document prototypes.
  • svg/path elements are registered with dominative and backed by a self-contained dim overlay (no extra native dependency); the dim extends through the top safe area. On iOS the cutout is a rounded-corner hole punched with an even-odd CAShapeLayer mask (matching driver's stageRadius); on Android it's a real rectangular hole left by four dim rectangles.
  • The overlay + popover are hosted on top of the app in a full-screen layer (RootLayout preferred), re-routing driver's document.body appends.
  • Native taps are translated into the capturing DOM click pipeline driver.js listens for, so the backdrop and popover buttons work unchanged.

Install

npm install @nstudio/nativescript-driver driver.js dominative @triniwiz/nativescript-masonkit

Usage (React)

Import the plugin once, after your dominative element registration, so the DOM layer installs and svg/path are registered:

// src/native/index.tsx
import './dom';                 // your dominative element registration
import '@nstudio/nativescript-driver'; // installs the DOM compat layer

Required: wrap your app root in a NativeScript RootLayout. The overlay mounts into it as a true full-window layer; without it the cutout/popover are offset by the page's safe-area inset.

<rootlayout>
  <gridlayout>{/* your app */}</gridlayout>
</rootlayout>

Then use driver.js exactly like on the web:

import { driver } from '@nstudio/nativescript-driver';
// or the React hook:
import { useDriver } from '@nstudio/nativescript-driver/react';

function HelpButton({ saveRef }) {
  const tour = useDriver({
    showProgress: true,
    steps: [
      { element: saveRef.current, popover: { title: 'Save', description: 'Tap to save.', side: 'bottom' } },
      { element: '#profile-tab',  popover: { title: 'Profile', description: 'Your profile lives here.' } },
    ],
  });
  return <button text="Help" onTap={() => tour.drive()} />;
}

step.element accepts a dominative element reference (ref.current) or a simple #id selector (matched against the NativeScript view id).

Implementation notes

A few NativeScript/MasonKit specifics the adapter handles internally:

  • The overlay is hosted in a RootLayout (full window) as two stacked sibling layers - a dim layer and a popover layer above it - so the popover always renders on top of the dim (a single layer doesn't reliably z-order the MasonKit popover over the dim). A single hit-testing tap handler on the top layer routes taps to popover buttons / popover body / backdrop.
  • Deterministic popover positioning. driver's own side selection depends on async MasonKit text measurement and is non-deterministic on mobile (it flips the popover to different sides run-to-run). The adapter instead positions the popover from the cutout (target) rect it parses from the SVG path - honoring driver's preferred side first, then falling back to whichever side fits - so placement is stable and always on-screen. The popover stays hidden until its position settles, so it never visibly jumps.
  • Dim through the safe area. The host layers and dim view disable iOS view clipping so the dim extends up into the status/notch area, dimming the full window.
  • Rounded cutout (iOS). The dim is one full-window view whose layer.mask is an even-odd CAShapeLayer (outer rect + rounded-rect hole). NativeScript can reset layer.mask during its layout pass, so the mask is re-applied on the view's layoutChanged (and loaded) — otherwise the corners render square.
  • The popover is a MasonKit element that ignores AbsoluteLayout attached props and lays its own frame at an unstable origin (translating it relative to the layer origin intermittently parked it in the top-left corner). So the adapter wraps the popover in a core GridLayout holder and positions the holder via AbsoluteLayout (core layouts honor it reliably) — stable, with no transform feedback. The popover is revealed on its first valid placement (the placement is reliable, so there's no slow wait-for-stability). Its footer (progress + Prev/Next) is laid out as a column so the buttons stay within the narrow mobile popover width and never run off-screen (MasonKit doesn't honor flex-wrap).
  • Footer spacing/clipping. Three MasonKit text quirks are handled in the footer: (1) a MasonText <span> ignores margin, so the vertical gap between the progress text and the nav buttons is a fixed-height container spacer (containers honor height); (2) the nav row is left-aligned and content-sized (alignSelf: flex-start) rather than stretched, so the buttons pack together and the last button never sits flush against — and clip on — the popover's right edge; (3) MasonText under-measures its own text width, so the buttons get a minWidth to leave room for a wide final glyph (e.g. the "e" in "Done").
  • Footer button alignment & popover padding. The leading button ("Previous") is given padding-left: 0 (via the injected CSS, which is what actually wins for padding here — inline padding on these MasonKit nodes is dropped) so its text starts flush with the title/description/progress left edge. The popover's own content padding (the inset of all text/buttons from the white card edges) is set with the CSS padding shorthand — the wrapper is a MasonKit Div/Scroll that honors the padding shorthand but not the padding-top/right/bottom/left longhands (the longhands barely inset the content), so always pad it via the shorthand.
  • Type hierarchy via MasonText slots. driver builds the popover title as a <header> and the description as a <div> — both container elements that lay out their text as a child MasonTextNode. A MasonTextNode's attributed glyph run is fixed at creation (the framework constructs it with null attributes), so a font set on the container never re-renders the text — neither re-measuring nor swapping the node redraws it. The adapter therefore renders the title/description text through a child <span> (a MasonText view, which renders its own live font), routes the container's text writes into that span, and styles the span — yielding a bold title and a smaller description (matching the web hierarchy). Progress (<span>) and the buttons (<button>) are already MasonText, so they're styled directly. The adapter uses a deep selector walk to reach these nested nodes (MasonKit's own-instance querySelector finds only direct children).
  • Footer buttons render as text links, not boxed pills. MasonKit's Button, when nested in the footer's text-like container, clips the top of a bordered/filled box (the box can't be un-clipped via padding, clipsToBounds, or display:flex), and setting border/background/color on a MasonKit Button inline crashes (Mason bridges them as ObjC strings). So Prev/Next/Done are styled (via injected CSS) as colored text actions — which can't be clipped and read cleanly on mobile.
  • Button hit-testing is region-based. The nested MasonKit footer buttons don't report a window location and don't receive native taps, so Next/Prev/Done are located by the navigation row's frame plus accumulated button widths (DOM order Prev→Next).
  • Close button removed. driver's × close button renders as a stray bordered box in MasonKit (which honors neither display:none nor visibility:collapse here), so the adapter removes it from the popover. Dismiss via a backdrop tap or the Done button on the last step.
  • Robust teardown. When the tour ends (overlay + popover both removed), the host layers are immediately made non-interactive + collapsed and then removed on the next tick. The immediate step matters because teardown often runs inside the popover layer's own tap-gesture callback (a native backdrop dismiss), where removing a view from the tree is silently dropped by iOS - leaving an invisible touch-blocking layer that would freeze the page.
  • Popover text is set via the element's native text property + a child text node (driver's innerHTML/innerText are routed through this at document.createElement time).
  • Android footer buttons. A MasonKit Button nested in the footer's nav-row <span> (a Text) doesn't lay out or paint on Android (it collapses to zero size). So on Android the adapter renders the Prev/Next/Done labels as <span>s (a Text measures + paints reliably) inside a real <view> row, routes each real button's text into its label span (via the same text-slot mechanism used for the title/description), and routes a tap on a label back to the real (hidden) button so driver's click handler fires. iOS keeps laying the buttons out inside the nav-row span directly.
  • Android popover padding is 24 (vs 12 on iOS) — 12 reads too tight on Android. The nav-row label gap is a fixed-width spacer <view> (a Text span ignores margin).

Fidelity notes as of v1

  • Rounded cutout corners (stageRadius) are rendered on both platforms: iOS punches the hole with an even-odd CAShapeLayer mask; Android paints the dim and punches a rounded-rect hole with a PorterDuff.CLEAR foreground Drawable (a saveLayer + Canvas.drawRoundRect).
  • Popover arrow is not drawn (collapsed to avoid corrupting popover positioning).
  • Footer buttons are colored text actions, not boxed pills (MasonKit clips the button box and crashes on inline button border/background — see implementation notes).
  • Hole tap-through: tapping the spotlighted element counts as a backdrop tap (true pass-through needs a native hit-test). Matches driver's default close-on-overlay behavior.
  • Auto-scroll to a target (scrollIntoView) is supported on iOS, both down and up: the adapter scrolls the page's MasonKit Scroll container to center the target (skipping when it's already comfortably visible), and the cutout + popover follow. The scroll is animated (a ~340 ms requestAnimationFrame ease-in-out that drives the container's model contentOffset each frame — not the native setContentOffset(animated:), whose presentation-only animation would desync the cutout from the page since getLocationInWindow jumps straight to the final offset). To also scroll targets that sit near the very top under a sticky/floating header (which driver's own in-view check would skip), set driver's onHighlightStarted: (el) => el?.scrollIntoView({ block: 'center' }). On Android the same works via the native scroll container's smoothScrollTo (whose animation moves the real scroll position, so the cutout/popover track it natively).
  • Keyboard navigation (Esc/arrows/Tab) is a desktop concern and is a no-op on mobile.
  • Complex CSS selectors for step.element are best-effort; prefer element refs.

License

Apache-2.0