From 465eba62c590ec8cb1c5987296708ab583b09db8 Mon Sep 17 00:00:00 2001 From: Vishnu_Mac Date: Tue, 27 Jan 2026 15:42:34 +0530 Subject: [PATCH] feat: add 'Copy for AI' button to package page - Adds button to copy package info (name, version, description, install command, README) as markdown for AI context - Button positioned right of description on desktop, hidden on mobile - Secondary button in README section for mobile access - Uses theme-consistent btn/btn-ghost shortcuts - Fixed width (min-w-32) to prevent size change on copy feedback --- app/pages/[...package].vue | 145 ++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 28 deletions(-) diff --git a/app/pages/[...package].vue b/app/pages/[...package].vue index 26280187..c7f34065 100644 --- a/app/pages/[...package].vue +++ b/app/pages/[...package].vue @@ -334,6 +334,68 @@ async function copyInstallCommand() { setTimeout(() => (copied.value = false), 2000) } +// Copy for AI context +const copiedForAI = ref(false) +const aiContextText = computed(() => { + if (!pkg.value || !displayVersion.value) return '' + + const parts: string[] = [] + + // Package header + parts.push(`# ${pkg.value.name}@${displayVersion.value.version}`) + parts.push('') + + // Description + if (pkg.value.description) { + parts.push(`> ${pkg.value.description}`) + parts.push('') + } + + // Key info + parts.push('## Package Info') + parts.push(`- **Install:** \`${installCommand.value}\``) + if (pkg.value.license) parts.push(`- **License:** ${pkg.value.license}`) + if (repositoryUrl.value) parts.push(`- **Repository:** ${repositoryUrl.value}`) + if (homepageUrl.value) parts.push(`- **Homepage:** ${homepageUrl.value}`) + parts.push('') + + // Dependencies summary + // const depCount = getDependencyCount(displayVersion.value) + // if (depCount > 0) { + // parts.push(`## Dependencies (${depCount})`) + // const deps = displayVersion.value.dependencies + // if (deps) { + // parts.push(Object.entries(deps).map(([name, version]) => `- ${name}: ${version}`).join('\n')) + // } + // parts.push('') + // } + + // README content (extract text from HTML) + if (readmeData.value?.html) { + parts.push('## README') + parts.push('') + // Convert HTML to plain text (client-side only) + if (import.meta.client) { + const tempDiv = document.createElement('div') + tempDiv.innerHTML = readmeData.value.html + // Get text content, preserving some structure + const textContent = tempDiv.innerText || tempDiv.textContent || '' + parts.push(textContent.trim()) + } else { + parts.push('[README content available - copy from browser]') + } + } + + return parts.join('\n') +}) + +async function copyForAI() { + if (!aiContextText.value) return + await navigator.clipboard.writeText(aiContextText.value) + copiedForAI.value = true + setTimeout(() => (copiedForAI.value = false), 2000) +} + // Expandable description const descriptionExpanded = ref(false) const descriptionRef = ref() @@ -489,33 +551,47 @@ defineOgImageComponent('Package', { - -
-

- -

-

No description provided

- -
+ +
+ +
+

+ +

+

No description provided

+ +
+ +
+
+ + -
+
-
$ - + npm install {{ pkg.name }} + > +
@@ -862,9 +938,22 @@ defineOgImageComponent('Package', {
-

- Readme -

+
+

+ Readme +

+ + + +