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

dropgallery

v0.1.1

Published

CLI that turns a selection of images and videos into a single shareable static gallery hosted on S3 + CloudFront. macOS-first (Apple Shortcut → native dialogs) with a cross-platform terminal mode.

Readme

DropGallery

Turns a selection of images and videos into a shareable static gallery hosted on S3 + CloudFront. Built for macOS (native dialogs, Finder integration using Shortcuts), with a terminal mode for Linux and Windows.

Example DropGallery rendered in the browser

Motivation

There are countless tools for making photo galleries out there, and this is one of them! Why not use others?

Because this one does what I really, really want.

"So tell me what you want, what you really, really want"

  • Hosted on your own S3 bucket + CloudFront distribution
  • Uses your own domain (e.g. gallery.mydomain.com) with TLS
  • Produces statically-generated galleries (no server-side processing)
  • Generates random, unguessable URLs (easily shared, but still private)
  • Optional expiration (1, 7, 30 days, or never)
  • Supports images and videos
  • Minimal design - no logins, tracking, analytics, or other fluff
  • Easy to integrate into macOS Finder (and still usable on other platforms)

What it does

files → prompts (confirm / title / expiration)
      → sharp + ffmpeg generate thumbnails
      → render dark-themed PhotoSwipe gallery
      → upload to S3 with expire-days tags
      → CloudFront URL → clipboard
  • 1, 7, 30-day, or never expirations (S3 lifecycle rules do the actual deletion)
  • Dark-themed responsive grid with a click-to-expand lightbox (PhotoSwipe v5)
  • HEIC/HEIF/AVIF auto-converted to JPEG so non-Safari browsers display them
  • Videos get a real poster frame if ffmpeg is on PATH, a placeholder otherwise
  • One unguessable URL per gallery (128-bit base64url ID); URLs are the only access control
  • macOS: native dialogs + a Notification Center toast when work begins. Linux / Windows: terminal prompts + spinner

Prerequisites

  • Node.js ≥ 20.17 (current Node 20 LTS) on macOS, Linux, or Windows
  • AWS account + a way to attach a custom TLS domain
  • ffmpeg (optional but recommended for video posters):
  • macOS only: uses built-in osascript, pbcopy, open — no install needed

One-time AWS setup

1. S3 bucket

  • Create a new S3 bucket dedicated to this tool.
  • Block all public access (default).

2. CloudFront distribution

  • Create a CloudFront distribution.
  • Origin: the S3 bucket above. Use OAC (Origin Access Control) so the bucket stays private and only CloudFront can read it.
  • Custom TLS domain (e.g. gallery.yourdomain.com): attach an ACM certificate issued in us-east-1 (CloudFront only reads us-east-1 certs).
  • Error responses: configure 404 (and optionally 403) however you like. Don't enable directory listing — that would make /g/ enumerable.

No CloudFront Functions or Lambda@Edge needed; each gallery is plain static files. The HTML lands at the bare key g/<id> with Content-Type: text/html, and a <base href="<id>/"> makes its relative asset paths resolve correctly.

CloudFront propagation takes a few minutes globally; URLs may 404 until then.

3. Lifecycle rules (the expiration mechanism)

On the bucket, create three lifecycle rules. Each is filtered by exactly one tag value and expires matching objects:

| Rule name | Tag filter | Expire current versions after | | --------------- | ---------------- | ----------------------------- | | expire-1day | expire-days=1 | 1 day | | expire-7days | expire-days=7 | 7 days | | expire-30days | expire-days=30 | 30 days |

Do not create a bucket-wide catch-all rule — galleries with the "Never" expiration carry no tag, and a catch-all would defeat that.

4. IAM credentials

Create an IAM user (or role) with this minimal policy on the bucket:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:PutObjectTagging"],
      "Resource": "arn:aws:s3:::your-gallery-bucket/*"
    }
  ]
}

Add the access keys to ~/.aws/credentials under a named profile:

[gallery]
aws_access_key_id = AKIA...
aws_secret_access_key = ...

The script never reads raw access keys — it loads the profile via the AWS SDK.

Install

Once the AWS prerequisites above are in place:

npm install -g dropgallery
dropgallery --setup

dropgallery --setup walks you through the four config values (profile, region, bucket name, CloudFront domain), validates them against AWS by uploading a tagged test object, and writes the config to the OS-standard application config directory:

  • macOS: ~/Library/Preferences/dropgallery/galleryrc
  • Linux: ~/.config/dropgallery/galleryrc
  • Windows: %APPDATA%\dropgallery\Config\galleryrc

If you prefer a dotfile in your home directory, ~/.galleryrc is checked first and wins if it exists. The format is INI, with one [section] per destination:

[default]
AWS_PROFILE=gallery
AWS_REGION=us-east-1
S3_BUCKET=your-gallery-bucket
CLOUDFRONT_DOMAIN=gallery.yourdomain.com

You can re-run dropgallery --setup any time to regenerate the config. Old flat-format configs (no [section] headers) from earlier versions still work — they're treated as a single destination named default.

Multiple destinations

If you wanna upload to more than one bucket / domain — e.g. one for personal galleries and one for work — add additional sections:

[personal]
AWS_PROFILE=gallery
AWS_REGION=us-east-1
S3_BUCKET=personal-gallery-bucket
CLOUDFRONT_DOMAIN=gallery.mydomain.com

[work]
AWS_PROFILE=work-gallery
AWS_REGION=us-west-2
S3_BUCKET=work-gallery-bucket
CLOUDFRONT_DOMAIN=gallery.work.example.com

Each section is independent — its own AWS profile, region, bucket, and CloudFront domain. Each bucket needs the same one-time setup (lifecycle rules, IAM policy, CloudFront distribution).

To add a new destination via the wizard:

dropgallery --setup --as work

This adds (or replaces) just the [work] section, leaving any other destinations untouched.

With one destination configured, DropGallery uses it automatically. With more than one, you're prompted — or skip the prompt with --target <name> or DROPGALLERY_TARGET=<name>:

dropgallery --target work path/to/photo.jpg

Installing from source (development)

git clone https://github.com/turley/dropgallery.git
cd dropgallery
npm install
node gallery.js --setup        # or: npm link && dropgallery --setup
npm test                       # smoke tests, no AWS required

Apple Shortcut (macOS)

A small wrapper that hands the selected Finder files (or folders) to the dropgallery command.

Install

Open Make Gallery (iCloud Shortcut) on macOS 12 or later, then tap Add Shortcut. You may need to manually allow the shortcut to access the shell action. That's it.

The shortcut is a single Run Shell Script action:

export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.nvm/versions/node/$(ls $HOME/.nvm/versions/node 2>/dev/null | tail -1)/bin:$PATH"
dropgallery "$@"

The PATH line covers the three places npm install -g typically puts the bin (Apple Silicon Homebrew, Intel Homebrew / system Node, nvm). If which dropgallery shows yours somewhere else, edit the shortcut after installing — or build it yourself.

  1. Open Shortcuts.appFile → New Shortcut (or click +).
  2. Name it Make Gallery (or whatever you want).
  3. Click the small ("info") button in the editor's toolbar to reveal the Shortcut Details panel on the right.
  4. In the Details panel:
    • Toggle on Use as Quick Action.
    • Check Finder.
    • Set Receive to Files, Folders (so the action shows up for both individual files and folders selected in Finder).
  5. macOS auto-inserts a Receive [Files and Folders] from Quick Actions step at the top of the workflow. Leave it.
  6. Drag a Run Shell Script action below it. Configure:
    • Shell: zsh
    • Pass Input: as arguments
    • Script: the two lines shown above.
    • Input: Shortcut Input with Pass input as arguments.
  7. Save (⌘S).

Use it

Select files or a folder in Finder, right-click → Quick ActionsMake Gallery (it may live under a "More…" submenu if you have a lot of Quick Actions). Folders are walked recursively for supported extensions (.jpg/.jpeg/.png/.webp/.gif/.heic/.heif/.avif/.mp4/.mov); a mix of files and folders is fine.

Make Gallery in the Finder Quick Actions menu

Optional: keyboard shortcut

In Shortcuts.app, select Make Gallery, open the Details panel, and click Add Keyboard Shortcut — assign something like ⌥⌘G.

Usage from terminal

dropgallery path/to/a.jpg path/to/b.heic path/to/c.mp4
dropgallery path/to/folder                          # walks recursively
dropgallery folder1 folder2 some-loose-photo.jpg    # mix is fine
dropgallery --help                                  # full flag list

On macOS, this runs the same native dialogs as the Apple Shortcut. To force the terminal UI instead (e.g. for unattended scripts), pass --cli:

dropgallery --cli path/to/a.jpg path/to/b.heic

Linux and Windows always use the terminal UI. DROPGALLERY_UI=cli is equivalent to --cli on macOS.

With multiple destinations configured, --target <name> (or DROPGALLERY_TARGET=<name>) skips the destination prompt:

dropgallery --target work path/to/a.jpg

Logs

Every run writes a timestamped .log file in the OS-appropriate log directory:

  • macOS: ~/Library/Logs/dropgallery/
  • Linux: ~/.local/share/dropgallery/log/
  • Windows: %LOCALAPPDATA%\dropgallery\Log\

Successful runs are short; failures include full stack traces and any captured ffmpeg / AWS SDK output. Logs aren't rotated — clean them out periodically if you care.

Troubleshooting

  • "AWS auth failure" / SignatureDoesNotMatch — check the AWS_PROFILE in the relevant [section] of your config (~/Library/Preferences/dropgallery/galleryrc on macOS) matches a section in ~/.aws/credentials, and that the IAM policy grants s3:PutObject and s3:PutObjectTagging on the bucket. Re-run dropgallery --setup --as <name> to validate.
  • Destination "X" not found — edit the relevant [X] section of your config file, or run dropgallery --setup --as X to create it.
  • Gallery URL returns 404 — CloudFront takes 5–15 minutes to deploy after first creation. After that, check OAC is correctly attached and the bucket policy grants read to the CloudFront principal. Verify the gallery's HTML object exists in the S3 console at the bare key g/<id> (no extension).
  • Object not expiring — check the lifecycle rule's tag filter is expire-days=N (no spaces); inspect actual tags via aws s3api get-object-tagging --bucket your-gallery-bucket --key g/<id> (the HTML) or --key g/<id>/images/<file> (an asset).
  • Videos show placeholder instead of poster frame — ffmpeg isn't on $PATH in the Shortcut's minimal environment. Install it into /usr/local/bin or /opt/homebrew/bin, or set PATH explicitly in the shell-script action.
  • HEIC files fail to convert — sharp's prebuilt binaries should bundle libheif on macOS. If conversion fails, run npm rebuild sharp or npm install sharp@latest.
  • (Linux) clipboardy errors with "Cannot find xclip / wl-copy" — install one: apt install xclip (X11) or apt install wl-clipboard (Wayland).
  • (Linux) Sharp install failed — older glibc / Alpine setups may need npm install --include=optional sharp or system libvips. See sharp's install docs.

Out of scope (intentionally)

  • Password-protected galleries / signed URLs — URL unguessability is the only access control
  • Analytics, upload UI, albums, EXIF display, sort by date
  • Automatic log rotation

License

MIT — see LICENSE.

PhotoSwipe v5 is vendored in templates/photoswipe/ and retains its own MIT license — see templates/photoswipe/LICENSE.