Skip to content

feat: add Share button to package pages via Web Share API - #2997

Open
tomayac wants to merge 11 commits into
npmx-dev:mainfrom
tomayac:feat/web-share-button
Open

feat: add Share button to package pages via Web Share API#2997
tomayac wants to merge 11 commits into
npmx-dev:mainfrom
tomayac:feat/web-share-button

Conversation

@tomayac

@tomayactomayac commented Jul 4, 2026

Copy link
Copy Markdown

Summary

  • Adds a share button to the package header that uses navigator.share(), attaching the package's OG image when the full payload (title/text/url/files) is confirmed shareable, falling back to url/text-only otherwise.
  • Adds a v keyboard shortcut and a command palette entry for sharing the current package.
image

Split out of #2982 per review feedback to keep that PR focused on PWA/WCO/badging.

Test plan

  • pnpm test:unit — 1673/1673 passing
  • pnpm run test:types — passing
  • vp lint — 0 errors
  • node scripts/find-invalid-translations.ts — no missing/unused i18n keys
  • knip — clean

Adds a share button to the package header using navigator.share(),
with a fallback to url/text-only sharing when the OG image can't be
attached, plus a "v" keyboard shortcut and a command palette entry.
@vercel

vercelBot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
npmx.devReadyReadyPreviewAug 27, 2026 10:03pm
2 Skipped Deployments
ProjectDeploymentActionsUpdated (UTC)
docs.npmx.devIgnoredIgnoredPreviewAug 27, 2026 10:03pm
npmx-lunariaIgnoredIgnoredAug 27, 2026 10:03pm

Request Review

@coderabbitai

coderabbitaiBot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added native package sharing through the header and command palette when supported.
    • Share includes package details, the current URL and, where available, an Open Graph image.
    • Added programmatic button activation support.
  • Documentation

    • Added translation strings for sharing labels and accessibility text.
  • Tests

    • Updated accessibility checks for environments without Web Share support.

Walkthrough

This PR adds native Web Share support for package pages through a client-only button and header actions. It exposes programmatic button clicks and adds translation, schema, and accessibility test updates.

Changes

Package Web Share feature

Layer / File(s)Summary
Expose click() on ButtonBase
app/components/Button/Base.vue
Adds a click() method to the exposed instance API and delegates to the underlying element.
Share button implementation
app/components/Package/ShareButton.client.vue
Detects Web Share support, optionally adds an og:image file, supports the v keyboard shortcut, and calls navigator.share().
Package header integration
app/components/Package/Header.vue
Adds package sharing detection and actions, a conditional command palette entry, and the share button UI.
Translation and accessibility support
i18n/locales/en.json, i18n/schema.json, test/unit/a11y-component-coverage.spec.ts
Adds sharing translation keys and schema fields, and skips the client-only share button in accessibility coverage.

Sequence Diagram(s)

sequenceDiagram
participant User
participant Header as Package/Header.vue
participant ShareButton as PackageShareButton
participant NavigatorShare as navigator.share
User->>Header: select package share action
Header->>Header: call sharePackage()
Header->>NavigatorShare: share package metadata and current URL
User->>ShareButton: click or press "v"
ShareButton->>ShareButton: build ShareData and optional image file
ShareButton->>NavigatorShare: share(shareData)
NavigatorShare-->>ShareButton: resolve or reject
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarises the main change: adding a package share button using the Web Share API.
Description check✅ PassedThe description directly explains the share button, Web Share API behaviour, keyboard shortcut, command palette entry, and validation results.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actionsBot commented Jul 4, 2026

Copy link
Copy Markdown

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

FileNote
i18n/locales/en.jsonSource changed, localizations will be marked as outdated.
Warnings reference
IconDescription
🔄️The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@codecov

codecovBot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 30.00000% with 28 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing linesPatch %Lines
app/components/Package/ShareButton.client.vue24.24%17 Missing and 8 partials ⚠️
app/components/Package/Header.vue60.00%2 Missing ⚠️
app/components/Button/Base.vue0.00%1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
app/components/Package/ShareButton.client.vue (2)

22-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for share fallback logic.

getOgImageFile() and share() contain the core fallback behaviour (capability probing, fetch failure handling, combined-payload gating) but no unit tests were added for this new logic.

As per path instructions, **/*.{test,spec}.{ts,tsx} should "Write unit tests for core functionality using vitest"; this new share logic is a good candidate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/components/Package/ShareButton.client.vue` around lines 22 - 36, Add unit
tests for the share fallback behavior around getOgImageFile() and share() in
ShareButton.client.vue, since the new capability probing, fetch/error handling,
and combined-payload gating are not covered. Create vitest tests that exercise
the main branches: navigator.canShare unavailable/false returns null, og:image
missing or non-image content returns null, fetch failure is swallowed, and
share() only includes the OG file when getOgImageFile() succeeds. Use the
existing ShareButton.client.vue logic and its getOgImageFile/share symbols to
locate the code.

Source: Path instructions


57-57: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Silently swallowing all share errors.

.catch(() => {}) suppresses both expected cancellations (AbortError) and genuine failures. Consider only swallowing AbortError and logging other errors for observability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/components/Package/ShareButton.client.vue` at line 57, The share flow in
ShareButton.client.vue is swallowing every error from navigator.share, which
hides real failures. Update the share handling around navigator.share(shareData)
to only ignore expected AbortError cancellations, and route any other errors to
the existing logging/observability path so genuine share failures can be
diagnosed. Use the ShareButton component and its shareData/navigator.share call
site to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/components/Package/ShareButton.client.vue`:
- Around line 38-58: The package share payload is implemented inconsistently
between ShareButton.client.vue and Header.vue, so the command-palette share
action uses a less rich ShareData than the button and shortcut. Extract the
shared share flow into a common helper/composable such as usePackageShare() that
builds the title/text/url payload, optionally attaches the og:image file when
navigator.canShare allows it, and is reused by both share() and Header.vue's
sharePackage() so all entry points behave the same.
- Around line 10-20: The ShareButton.client.vue keyboard handler currently uses
a raw onKeyStroke, so the v shortcut ignores the global shortcuts toggle. Update
the shortcut registration in the ShareButton component to go through
useShortcuts(), matching the pattern used by Header.vue and other package
actions, and keep the existing isKeyWithoutModifiers / isEditableElement checks
and buttonRef click behavior intact.
---
Nitpick comments:
In `@app/components/Package/ShareButton.client.vue`:
- Around line 22-36: Add unit tests for the share fallback behavior around
getOgImageFile() and share() in ShareButton.client.vue, since the new capability
probing, fetch/error handling, and combined-payload gating are not covered.
Create vitest tests that exercise the main branches: navigator.canShare
unavailable/false returns null, og:image missing or non-image content returns
null, fetch failure is swallowed, and share() only includes the OG file when
getOgImageFile() succeeds. Use the existing ShareButton.client.vue logic and its
getOgImageFile/share symbols to locate the code.
- Line 57: The share flow in ShareButton.client.vue is swallowing every error
from navigator.share, which hides real failures. Update the share handling
around navigator.share(shareData) to only ignore expected AbortError
cancellations, and route any other errors to the existing logging/observability
path so genuine share failures can be diagnosed. Use the ShareButton component
and its shareData/navigator.share call site to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26b214ff-9bba-4cf5-875b-f3a1d72c6e10

📥 Commits

Reviewing files that changed from the base of the PR and between e731a54 and 8d40798.

📒 Files selected for processing (6)
  • app/components/Button/Base.vue
  • app/components/Package/Header.vue
  • app/components/Package/ShareButton.client.vue
  • i18n/locales/en.json
  • i18n/schema.json
  • test/unit/a11y-component-coverage.spec.ts

Comment threadapp/components/Package/ShareButton.client.vue
Comment threadapp/components/Package/ShareButton.client.vue
tomayac added a commit to tomayac/npmx.dev that referenced this pull request Jul 4, 2026
…ate PRs
Per review feedback, keeps this PR focused on PWA installability, WCO,
and app badging. The Share button and native switch polyfill now live
in npmx-dev#2997 and npmx-dev#2998 respectively.
@ghostdevvghostdevv added the needs discussion An idea that needs more discussion to understand the scope and impact. label Jul 6, 2026
@tomayac

Copy link
Copy Markdown
Author

CC: @userquin per his request :-)

chore: add spinner icon while sharing
@userquin

userquin commented Aug 27, 2026

Copy link
Copy Markdown
Member

We still need to add the v shorcut to the keyboard shortcuts dialog/popup, right now the share button registering the shortcut, works with ctrl + K + searching share + clicking share entry.

I'm working on it.

@userquin

Copy link
Copy Markdown
Member

v shortcut now at app footer (reusing the package.share_aria_label entry)

I'm adding support to disable shorcut when shorcuts disabled at settings.

@userquinuserquin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice ❤️

:classicon="sharing ? 'i-svg-spinners:ring-resize' : 'i-lucide:share-2'"
:aria-label="$t('package.share_aria_label', { package: packageName })"
:aria-keyshortcuts="'v'"
:ariaKeyshortcuts="keyboardShortcutsEnabled ? 'v' : undefined"

@userquinuserquinAug 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I press Ctrl + click in my IDE (WebStorm) on :aria-keyshortcuts, it redirects to the DOM library, when it should redirect to the ariaKeyshortcuts prop of the Button component.

@danielroe

Copy link
Copy Markdown
Member

this is beautifully done

my main worry is:

  • is sharing something that is a common enough need for people browsing a package registry that we need a dedicated keyboard shortcut and button, in an interface which is already getting a little crowded?

@tomayac

Copy link
Copy Markdown
Author

this is beautifully done

<3

my main worry is:

  • is sharing something that is a common enough need for people browsing a package registry that we need a dedicated keyboard shortcut and button, in an interface which is already getting a little crowded?

I work in DevRel, and I have this need constantly. People ask me all the time what package they should use for x, y, or z, and especially on the socials where the Share feature lets me share directly with apps on mobile. (The desktop share experience is more limited by default — but I use Shareful — on mobile it's amazing.)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs discussionAn idea that needs more discussion to understand the scope and impact.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@tomayac@userquin@danielroe@ghostdevv