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

progress-notifications

v0.0.2

Published

Custom progress notifications with Rich UI, action buttons, and RTL support.

Readme

progress-notifications

A Capacitor plugin for showing download/upload progress from your app to the user — natively, while the app is in the background or the device is locked.

| Platform | Mechanism | Surfaces | |---|---|---| | iOS 16.2+ | Live Activity via ActivityKit | Lock screen banner, Dynamic Island (compact / minimal / expanded) | | iOS < 16.2 | (rejected) | API calls reject with a clear error — no fallback | | Android | Foreground progress notification via NotificationCompat.Builder.setProgress | Notification shade, lock screen, status bar icon | | Web | No-op stubs (logs to console) | — |


Install

npm install progress-notifications
npx cap sync

Then follow the platform setup sections below.


iOS setup

1. Info.plist

Add NSSupportsLiveActivities to your app's ios/App/App/Info.plist:

<key>NSSupportsLiveActivities</key>
<true/>

Without this, Activity.request(...) silently fails.

2. Add a Widget Extension target

Live Activities require a Widget Extension target inside your app's Xcode project — Capacitor plugins can't ship one. One-time setup, ~3 minutes.

  1. Open your app's workspace: npx cap open ios.
  2. File → New → Target…
  3. Pick Widget ExtensionNext.
  4. Fill in:
    • Product Name: ProgressLiveActivity (any name works)
    • Include Live Activity: ✓ check this box
    • Include Configuration Intent: leave unchecked
  5. Click Finish. If asked "Activate scheme?", click Cancel.
  6. Xcode generates a folder under ios/App/<ProductName>/. Delete the auto-generated <ProductName>LiveActivity.swift and <ProductName>Bundle.swift files inside it (otherwise you'll get a duplicate @main error). Keep Info.plist and Assets.xcassets.
  7. Drag the two files from node_modules/progress-notifications/widget-extension-template/ into the new target's folder in Xcode:
    • ProgressActivityAttributes.swift
    • ProgressLiveActivityWidget.swift
    • In the dialog: Copy items if needed = ON, Add to targets = your widget target only (NOT the App target).
  8. Build (⌘B). Should succeed.

Why are there two copies of ProgressActivityAttributes? ActivityKit requires the attribute struct to exist in both the plugin (so it can call Activity.request) and the widget extension (so it can render). The two definitions must stay byte-for-byte identical — they're matched at runtime via Codable serialization. If you add or rename a field in one, change the other.

3. iOS deployment target

The plugin's deployment target is iOS 15.0. Live Activities themselves require iOS 16.2 at runtime — gated automatically. Make sure your app's minimum deployment target is 15.0 or higher (App target → General → Minimum Deployments in Xcode).


Android setup

1. Notification permission (Android 13+)

Android 13+ requires runtime permission for notifications. Add to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Then request the permission at runtime before calling show() for the first time. Most apps use @capacitor/local-notifications's requestPermissions() for this since it's already a permission request flow your users know.

2. Custom icon (optional)

If you want a custom small icon (instead of the system download icon), drop a drawable into android/app/src/main/res/drawable/ (e.g. ic_stat_download.xml) and pass its name as androidIcon:

ProgressNotification.show({ id: 1, androidIcon: 'ic_stat_download', ... });

If the name doesn't resolve, the plugin falls back to android.R.drawable.stat_sys_download silently.

3. Foreground service (optional)

If your work continues while the app is fully backgrounded for more than ~5 minutes, pair this plugin with a foreground service (e.g. @capawesome-team/capacitor-android-foreground-service) so Android doesn't kill your process before progress completes.


Usage

import { ProgressNotification } from 'progress-notifications';

// Start the activity / notification
await ProgressNotification.show({
  id: 10,
  title: 'Downloading album',
  subtitle: 'Adele — 30',
  body: 'Track 4 of 12',
  icon: 'arrow.down.circle.fill',     // iOS: SF Symbol name
  androidIcon: 'ic_stat_download',    // Android: drawable resource name
});

// Update — call as often as you like (silent, no banner re-presentation)
ProgressNotification.update({
  id: 10,
  progress: 42,                        // 0-100
  body: 'Track 5 of 12',               // optional override
});

// Mark done — Live Activity dismisses itself, notification flips to "complete" state
ProgressNotification.complete({
  id: 10,
  title: 'Download complete',
  body: 'Tap to open',
  icon: 'checkmark.circle.fill',
  androidIcon: 'ic_stat_done',
});

API

show(options)

Starts a new progress activity / notification.

| Field | Type | Required | Notes | |---|---|---|---| | id | number | ✓ | Unique identifier. Pass the same id to update/complete to target this activity. | | title | string | | Defaults to "Downloading...". | | subtitle | string | | Rendered under the title. | | body | string | | Rendered under the progress bar (iOS) / as content text (Android). | | icon | string | | iOS: SF Symbol name (e.g. "arrow.down.circle.fill"). Default: "arrow.down.circle.fill". Ignored on Android. | | androidIcon | string | | Android: drawable resource name in your app (e.g. "ic_stat_download"). Default: system download icon. Ignored on iOS. |

Returns Promise<void>. Rejects on iOS < 16.2, when Live Activities are disabled in Settings, or if Activity.request fails.

update(options)

Updates the progress and (optionally) any text. Cached values from show are reused for any field you omit.

| Field | Type | Required | Notes | |---|---|---|---| | id | number | ✓ | Must match a prior show() call. | | progress | number | ✓ | 0–100. | | title / subtitle / body | string | | Optional overrides; previous value kept if omitted. |

Rejects if no activity is running for the given id.

complete(options)

Marks the activity / notification as complete. iOS dismisses the Live Activity per the system's default policy. Android keeps a final notification with no progress bar.

| Field | Type | Required | Notes | |---|---|---|---| | id | number | ✓ | | | title / subtitle / body | string | | Optional final text. | | icon / androidIcon | string | | Optional final icon (e.g. "checkmark.circle.fill"). |


Limitations & quirks

iOS

  • Live Activities require iOS 16.2+. Older iOS rejects.
  • Live Activities can be disabled by the user globally (Settings → Face ID & Passcode → Live Activities) or per-app (Settings → YourApp → Live Activities). When disabled, calls reject — handle this in your app.
  • The system limits how long a Live Activity can stay active (~8 hours, then auto-ends).
  • Updates from the app are limited to a few per second by the system. For very fast updates, throttle on the JS side.
  • The icon string must be a valid SF Symbol name. Invalid names render as blank — they don't throw.

Android

  • No Live Activity / Dynamic Island equivalent exists. The plugin uses a foreground progress notification, which appears in the notification shade and the lock screen.
  • The notification channel is created once at IMPORTANCE_LOW (no sound / vibration). The first show() posts the notification; subsequent update() calls revise it silently via setOnlyAlertOnce(true).
  • Calling update() or complete() without a prior show() rejects.

Both

  • The icon (iOS) and androidIcon (Android) namespaces are completely separate. Passing the same string to both fields will not work. SF Symbols are not Android drawables and vice versa.

Troubleshooting

iOS: "Cannot find type 'ProgressActivityAttributes' in scope" Xcode has a stale package cache. File → Packages → Reset Package Caches, then Product → Clean Build Folder.

iOS: Live Activity never appears

  • Confirm NSSupportsLiveActivities = true is in your app's Info.plist.
  • Confirm the user accepted the prompt and Live Activities aren't disabled in Settings.
  • Confirm the Widget Extension target was added and the two template Swift files were added to its target membership (not the App target).
  • Confirm there is no leftover auto-generated @main WidgetBundle from the Xcode template (deletion in step 6 of iOS setup).

iOS: Activity appears but never updates The two ProgressActivityAttributes definitions (plugin copy + widget copy) have diverged. Diff them — they must match field-for-field.

iOS: "Missing package product 'CapApp-SPM'" Your ios/App/CapApp-SPM/Package.swift was rewritten with an iOS deployment target newer than your swift-tools-version allows. Either lower the platform line or bump the swift-tools-version comment.

Android: notification doesn't appear on Android 13+ You haven't requested POST_NOTIFICATIONS runtime permission yet. See Android setup → Notification permission.

Android: androidIcon ignored The drawable name doesn't exist in your app's resources, or the resource ID lookup returned 0. Confirm the drawable is at android/app/src/main/res/drawable/<name>.xml and you passed exactly <name> (no extension, no path).


Local development

npm install
npm run build       # tsc + rollup → dist/
npm run lint

The plugin uses Capacitor's local-package linking. To consume it from another local app: add "progress-notifications": "file:../path/to/progress-notifications" to that app's package.json and run npm install.


License

See package.json.