Skip to content

feat: added feature to copy version number by hovering on the version#2660

Open
PreethiPantangi wants to merge 5 commits intonpmx-dev:mainfrom
PreethiPantangi:main
Open

feat: added feature to copy version number by hovering on the version#2660
PreethiPantangi wants to merge 5 commits intonpmx-dev:mainfrom
PreethiPantangi:main

Conversation

@PreethiPantangi
Copy link
Copy Markdown

🔗 Linked issue

Resolves #2646

🧭 Context

The feature provides user with the capability to copy the version number of the package by hovering over it. This helps the user to easily grab the data instead of typing it out.

Adds copy-to-clipboard functionality for package version via hover interaction in the header.

📚 Description

Files changed:
- Header.vue
- en.json

Header.vue
- The VersionSelector component is wrapped with CopyToClipboardButton component to ensure the user is able to copy the version number on hover.

en.json
- Added the key value pair "copy_version": "Copy version number" to ensure internationalization

No tests added as this change is UI-only and does not affect business logic. Existing functionality remains unchanged.

No documentation changes required

I've used co-pilot to understand some parts of the code before diving into making the feature changes

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 30, 2026

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

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment May 2, 2026 10:58pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview May 2, 2026 10:58pm
npmx-lunaria Ignored Ignored May 2, 2026 10:58pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added ability to copy resolved package version numbers to clipboard.
    • Enhanced version selector interaction with improved UI controls.
  • Localization

    • Added translation support for the new copy version feature.

Walkthrough

The changes add a copy-to-clipboard flow for the resolved package version: UI button integration around the version selector, clipboard logic and state, a command-palette command to copy the version, and new i18n keys/schema for the copy label.

Changes

Package header / version copy

Layer / File(s) Summary
Data / Display
app/components/Package/Header.vue
Adds resolvedVersionDisplay computed to normalise props.resolvedVersion for clipboard use.
Core Clipboard Logic
app/components/Package/Header.vue
Adds useClipboard instance exposing copiedPkgVersion, copyPkgVersion() that copies resolvedVersionDisplay.
State & Wiring
app/components/Package/Header.vue, app/components/VersionSelector.vue
Introduces versionSelectorRef, derives versionSelectorOpen, watches to set shouldHideCopyText while the selector is open; VersionSelector now exposes isOpen via defineExpose.
UI Integration
app/components/Package/Header.vue, app/components/CopyToClipboardButton.vue
Wraps VersionSelector with CopyToClipboardButton when resolved version exists; passes copiedPkgVersion, localized copy label, shouldHideCopyText, and triggers copyPkgVersion() on click. CopyToClipboardButton gains hideCopyText prop to conditionally hide label.
Command Palette
app/components/Package/Header.vue
Builds command list incrementally; conditionally adds package-copy-name and new package-copy-version command (copies version and announces result).
i18n strings
i18n/locales/en.json
Adds package.versions.copy_version label ("Copy version number").
i18n schema
i18n/schema.json
Adds copy_version property under package.versions.copy_alt in schema.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant UI as PackageHeader (UI)
  participant Version as VersionSelector
  participant Clipboard as ClipboardService
  participant Commands as CommandPalette
  participant Announcer as Announcer

  User->>UI: Click copy button / invoke "copy version" command
  UI->>Version: (reads) resolvedVersionDisplay / isOpen
  UI->>Clipboard: copy(resolvedVersionDisplay)
  Clipboard-->>UI: copy result
  UI->>Announcer: announce success/failure
  UI->>Commands: mark command as executed / update copied state
  Announcer-->>User: audible/ARIA feedback
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarises the main change: adding a copy-to-clipboard feature for package version numbers via hover interaction, which is the primary objective of the PR.
Description check ✅ Passed The description is directly related to the changeset, providing context, listing affected files (Header.vue, en.json), and explaining the implementation approach for the copy-version feature.
Linked Issues check ✅ Passed The PR fully addresses issue #2646 by implementing copy-to-clipboard functionality for package version via hover [#2646], improving visibility through the CopyToClipboardButton integration, and providing the requested quick-copy feature similar to package name functionality.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #2646 objectives: adding copy-version functionality (Header.vue, CopyToClipboardButton.vue, VersionSelector.vue), internationalisation (en.json, i18n/schema.json), and a reviewed behavioural refinement to hide the copy button during version selection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: Turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@github-actions
Copy link
Copy Markdown

Hello! Thank you for opening your first PR to npmx, @PreethiPantangi! 🚀

Here’s what will happen next:

  1. Our GitHub bots will run to check your changes.
    If they spot any issues you will see some error messages on this PR.
    Don’t hesitate to ask any questions if you’re not sure what these mean!

  2. In a few minutes, you’ll be able to see a preview of your changes on Vercel

  3. One or more of our maintainers will take a look and may ask you to make changes.
    We try to be responsive, but don’t worry if this takes a few days.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

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

File Note
i18n/locales/en.json Source changed, localizations will be marked as outdated.
Warnings reference
Icon Description
🔄️ 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
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

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

Files with missing lines Patch % Lines
app/components/Package/Header.vue 62.50% 7 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/Package/Header.vue`:
- Around line 108-118: The command object with id 'package-copy-version'
currently uses props.resolvedVersion directly for keywords and always calls
copyPkgVersion() and announce(), so guard these actions by checking that
props.resolvedVersion is a non-empty string before invoking copyPkgVersion() or
announce(); if absent, no-op (or return) so nothing is copied or announced; also
ensure keywords is strictly string-only by transforming/filtering it to a string
(e.g., use String(props.resolvedVersion) or include only when truthy) so
keywords never contains undefined/null; apply the same guard/keyword fix for the
other similar command block that uses resolvedVersion (the block noted in the
review).
🪄 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: 82e6bf58-7e13-4dac-81e0-768c1f1550c6

📥 Commits

Reviewing files that changed from the base of the PR and between 5eab00c and 8ed5b85.

📒 Files selected for processing (2)
  • app/components/Package/Header.vue
  • i18n/locales/en.json

Comment thread app/components/Package/Header.vue Outdated
Copy link
Copy Markdown
Contributor

@gameroman gameroman left a comment

Choose a reason for hiding this comment

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

Overall very good, just make sure the CI passes, also left a couple of comments

Comment thread app/components/Package/Header.vue Outdated
import type { RouteLocationRaw } from 'vue-router'
import type { CommandPaletteContextCommandInput } from '~/types/command-palette'
import { SCROLL_TO_TOP_THRESHOLD } from '~/composables/useScrollToTop'
import { useClipboard } from '@vueuse/core'
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.

iirc, this isn't necessary since it's auto-imported

Comment thread app/components/Package/Header.vue Outdated
announce($t('command_palette.announcements.copied_to_clipboard'))
},
},
]
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.

coderabbit's suggestions makes sense here. I would do something like this here

const commands: CommandPaletteContextCommandInput[] = []

if (packageName.value) {
  commands.push({
    id: 'package-copy-name',
    group: 'package',
    label: $t('package.copy_name'),
    keywords: [packageName.value],
    iconClass: 'i-lucide:copy',
    action: () => {
      copyPkgName()
      announce($t('command_palette.announcements.copied_to_clipboard'))
    },
  })
}

if (props.resolvedVersion) {
  commands.push({
    id: 'package-copy-version',
    group: 'package',
    label: $t('package.versions.copy_version'),
    keywords: [props.resolvedVersion],
    iconClass: 'i-lucide:copy',
    action: () => {
      copyPkgVersion()
      announce($t('command_palette.announcements.copied_to_clipboard'))
    },
  })
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
app/components/Package/Header.vue (1)

312-328: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Gate the version copy wrapper to avoid an empty focusable control.

On Line 312, CopyToClipboardButton always renders, while VersionSelector is gated on Line 320. When version data is missing, this can leave a focusable copy control with no visible anchor and an empty copy value. Apply the same v-if to the wrapper and render VersionSelector unconditionally inside it.

Suggested fix
-          <CopyToClipboardButton
+          <CopyToClipboardButton
+            v-if="resolvedVersion && pkg?.versions && pkg?.['dist-tags']"
             :copied="copiedPkgVersion"
             :copy-text="$t('package.versions.copy_version')"
             class="inline-flex items-center min-w-0"
             `@click`="copyPkgVersion()"
           >
             <!-- Version selector -->
             <VersionSelector
-              v-if="resolvedVersion && pkg?.versions && pkg?.['dist-tags']"
               :package-name="packageName"
               :current-version="resolvedVersion"
               :versions="pkg.versions"
               :dist-tags="pkg['dist-tags']"
               :url-pattern="versionUrlPattern"
               position-class="max-md:inset-is-0 md:inset-ie-0"
             />
           </CopyToClipboardButton>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/Package/Header.vue` around lines 312 - 328, The
CopyToClipboardButton wrapper is rendered unconditionally while VersionSelector
is gated by resolvedVersion and pkg data, causing an empty focusable control;
change the v-if condition from the inner VersionSelector to the
CopyToClipboardButton so the whole wrapper (the CopyToClipboardButton that uses
copiedPkgVersion and copyPkgVersion) only renders when resolvedVersion &&
pkg?.versions && pkg?.['dist-tags'] are present, and then render VersionSelector
unconditionally inside it (keep props: :package-name="packageName",
:current-version="resolvedVersion", :versions="pkg.versions",
:dist-tags="pkg['dist-tags']", :url-pattern="versionUrlPattern").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/components/Package/Header.vue`:
- Around line 312-328: The CopyToClipboardButton wrapper is rendered
unconditionally while VersionSelector is gated by resolvedVersion and pkg data,
causing an empty focusable control; change the v-if condition from the inner
VersionSelector to the CopyToClipboardButton so the whole wrapper (the
CopyToClipboardButton that uses copiedPkgVersion and copyPkgVersion) only
renders when resolvedVersion && pkg?.versions && pkg?.['dist-tags'] are present,
and then render VersionSelector unconditionally inside it (keep props:
:package-name="packageName", :current-version="resolvedVersion",
:versions="pkg.versions", :dist-tags="pkg['dist-tags']",
:url-pattern="versionUrlPattern").

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46061c25-5981-4e68-bad1-638fc48efcfb

📥 Commits

Reviewing files that changed from the base of the PR and between 8ed5b85 and deb30ac.

📒 Files selected for processing (4)
  • Please
  • [👉
  • app/components/Package/Header.vue
  • i18n/schema.json
✅ Files skipped from review due to trivial changes (1)
  • i18n/schema.json

Comment thread [👉 Outdated
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.

This is accidental probably?

Comment thread Please Outdated
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.

This too

@PreethiPantangi PreethiPantangi changed the title Added feature to copy version number by hovering on the version feat: Added feature to copy version number by hovering on the version Apr 30, 2026
@PreethiPantangi PreethiPantangi changed the title feat: Added feature to copy version number by hovering on the version feat: added feature to copy version number by hovering on the version Apr 30, 2026
@PreethiPantangi
Copy link
Copy Markdown
Author

@gameroman I’ve addressed the feedback from the code review. Also, deleted the two junk files created accidentally.

@gameroman
Copy link
Copy Markdown
Contributor

Looks good

One thing to change, it probably should hide the copy button when you press the button to change the version

image

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
app/components/VersionSelector.vue (1)

527-530: ⚡ Quick win

Consider exposing isOpen as readonly to enforce a read-only API contract.

The parent (Header.vue) only needs to read isOpen to decide whether to hide the copy button. Exposing the raw ShallowRef gives any consumer the ability to write to it (versionSelectorRef.value.isOpen.value = …), which could silently clobber the component's internal dropdown state.

♻️ Proposed refactor
-// Expose isOpen state to parent components
-defineExpose({
-  isOpen,
-})
+// Expose isOpen state to parent components (read-only to prevent external mutation)
+defineExpose({
+  isOpen: readonly(isOpen),
+})

readonly is a Vue core built-in — no additional import required in <script setup>.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/VersionSelector.vue` around lines 527 - 530, The component
currently exposes the mutable ShallowRef isOpen via defineExpose, allowing
consumers to mutate the dropdown state; change the exposure to a read-only API
by wrapping isOpen with Vue's readonly and exposing that instead (keep the
internal isOpen ref unchanged) so consumers like Header.vue can only read the
state; use the built-in readonly in the <script setup> and update the
defineExpose call to export the readonly wrapper (no extra imports required).
app/components/CopyToClipboardButton.vue (1)

15-15: ⚡ Quick win

hideCopyText is a misleading prop name — it removes the entire button, not just its text.

v-if="!hideCopyText" is placed on the <button> element itself, so when the prop is true the full button (icon + label) is removed from the DOM. A reader of the prop API will expect hideCopyText to suppress only the text portion while keeping the icon or the button shell. Consider renaming to hideButton or hideCopyButton to accurately reflect what it controls.

♻️ Proposed rename
-  hideCopyText?: boolean
+  hideButton?: boolean
-      v-if="!hideCopyText"
+      v-if="!hideButton"

In Header.vue, update the bound prop name correspondingly:

-  :hideCopyText="shouldHideCopyText"
+  :hideButton="shouldHideCopyText"

Also applies to: 36-36

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/CopyToClipboardButton.vue` at line 15, The prop hideCopyText
on CopyToClipboardButton.vue is misleading because v-if="!hideCopyText" removes
the entire <button>; rename the prop to a clearer name such as hideButton or
hideCopyButton, update the prop declaration in CopyToClipboardButton.vue
(replace hideCopyText with the new name), change the v-if on the <button> to use
the new prop, and update every caller (e.g., Header.vue) to bind the new prop
name so behavior and API are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/components/CopyToClipboardButton.vue`:
- Line 15: The prop hideCopyText on CopyToClipboardButton.vue is misleading
because v-if="!hideCopyText" removes the entire <button>; rename the prop to a
clearer name such as hideButton or hideCopyButton, update the prop declaration
in CopyToClipboardButton.vue (replace hideCopyText with the new name), change
the v-if on the <button> to use the new prop, and update every caller (e.g.,
Header.vue) to bind the new prop name so behavior and API are consistent.

In `@app/components/VersionSelector.vue`:
- Around line 527-530: The component currently exposes the mutable ShallowRef
isOpen via defineExpose, allowing consumers to mutate the dropdown state; change
the exposure to a read-only API by wrapping isOpen with Vue's readonly and
exposing that instead (keep the internal isOpen ref unchanged) so consumers like
Header.vue can only read the state; use the built-in readonly in the <script
setup> and update the defineExpose call to export the readonly wrapper (no extra
imports required).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 30c44452-918f-465f-ad6c-8bcde988d98e

📥 Commits

Reviewing files that changed from the base of the PR and between deb30ac and 698c6d9.

📒 Files selected for processing (3)
  • app/components/CopyToClipboardButton.vue
  • app/components/Package/Header.vue
  • app/components/VersionSelector.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/Package/Header.vue

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A quick way to copy package version

2 participants