Skip to content

Conversation

@imrofayel
Copy link
Contributor

This pull request enhances the user experience for code blocks in the Readme.vue component by adding a copy-to-clipboard feature with visual feedback. The changes include frontend event handling, UI improvements for code block interactions, and backend support for rendering copy buttons in code blocks.

Frontend functionality and UI improvements:

  • Added a click event handler in Readme.vue to enable copying code blocks to the clipboard and provide visual feedback by toggling between copy and success icons.
  • Updated CSS styles in Readme.vue to position and animate the copy button, making it visible on code block hover and improving its appearance.

Backend rendering and sanitization:

  • Modified server/utils/readme.ts to render code blocks with a copy button by default, wrapping highlighted code in a .readme-code-block div and injecting a button for copy functionality.
  • Updated the list of allowed HTML tags and attributes in server/utils/readme.ts to support button elements and necessary attributes for the copy feature.

@vercel
Copy link

vercel bot commented Feb 1, 2026

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

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Feb 1, 2026 6:13pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 1, 2026 6:13pm
npmx-lunaria Ignored Ignored Feb 1, 2026 6:13pm

Request Review

opacity: 1;
}

.readme :deep(.readme-copy-button:hover) {
Copy link
Member

Choose a reason for hiding this comment

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

this should also be visible when focused 🙏

Copy link
Collaborator

Choose a reason for hiding this comment

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

👍🏼 works now

const html = highlightCodeSync(shiki, text, lang || 'text')
// Add copy button
return `<div class="readme-code-block" dir="ltr">
<button type="button" class="readme-copy-button" title="Copy code" check-icon="i-carbon:checkmark" copy-icon="i-carbon:copy" data-copy>
Copy link
Member

Choose a reason for hiding this comment

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

I think we need accessible text as well

Copy link
Member

@danielroe danielroe left a comment

Choose a reason for hiding this comment

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

thank you - this looks amazing!

I left some comments for a11y, and I think we'll also need to bump the readme cache key 🙏

@imrofayel
Copy link
Contributor Author

@danielroe hii! can you check now? ⟡˙⋆

Copy link
Collaborator

@serhalp serhalp left a comment

Choose a reason for hiding this comment

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

❤️ This is such a great little feature! I think all these little delightful touches in the app are how we make npmx shine and be successful!
💯

Comment on lines +23 to +32
const originalIcon = 'i-carbon:copy'
const successIcon = 'i-carbon:checkmark'

icon.classList.remove(originalIcon)
icon.classList.add(successIcon)

setTimeout(() => {
icon.classList.remove(successIcon)
icon.classList.add(originalIcon)
}, 2000)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if there's any way we could accomplish this with native CSS transitions?

renderer.code = ({ text, lang }: Tokens.Code) => {
return highlightCodeSync(shiki, text, lang || 'text')
const html = highlightCodeSync(shiki, text, lang || 'text')
// Add copy button
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Add copy button
// Add copy button
// NOTE: `app/components/Readme.vue` makes some assumptions about the exact DOM structured injected here.
// If you make changes here, you may need to update there as well.

opacity: 1;
}

.readme :deep(.readme-copy-button:hover) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍🏼 works now

Comment on lines +8 to +21
const handleCopy = async (e: MouseEvent) => {
const target = (e.target as HTMLElement).closest('[data-copy]')
if (!target) return

const wrapper = target.closest('.readme-code-block')
if (!wrapper) return

const pre = wrapper.querySelector('pre')
if (!pre?.textContent) return

await copy(pre.textContent)

const icon = target.querySelector('span')
if (!icon) return
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is making leaky assumptions about the DOM structure here, this feature seems like a prime candidate for some test coverage.

🙏🏼 Would you be up for opening a follow-up PR with at least a happy path test? Otherwise we can open an issue to track it.

@danielroe danielroe added this pull request to the merge queue Feb 1, 2026
Merged via the queue into npmx-dev:main with commit d714591 Feb 1, 2026
12 checks passed
@imrofayel imrofayel changed the title feat: add copy button functionality to code blocks in README feat: add copy button to code blocks in README Feb 2, 2026
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.

3 participants