@@ -108,11 +108,27 @@ function prefetchReadmeMarkdown() {
108108}
109109
110110async function copyReadmeHandler() {
111- await fetchReadmeMarkdown ()
111+ // Safari requires navigator.clipboard.write() to be called synchronously within
112+ // the user gesture, but accepts a Promise inside ClipboardItem.
113+ // This pattern works in Safari 13.1+, Chrome, and Firefox.
114+ if (typeof ClipboardItem !== ' undefined' && navigator .clipboard ?.write ) {
115+ const blobPromise = (async () => {
116+ await fetchReadmeMarkdown ()
117+ const markdown = readmeMarkdownData .value ?.markdown ?? ' '
118+ return new Blob ([markdown ], { type: ' text/plain' })
119+ })()
120+ try {
121+ await navigator .clipboard .write ([new ClipboardItem ({ ' text/plain' : blobPromise })])
122+ return
123+ } catch {
124+ // Fall through to legacy approach
125+ }
126+ }
112127
128+ // Legacy fallback (non-Safari, or older browsers)
129+ await fetchReadmeMarkdown ()
113130 const markdown = readmeMarkdownData .value ?.markdown
114131 if (! markdown ) return
115-
116132 await copyReadme (markdown )
117133}
118134
@@ -305,6 +321,17 @@ const latestVersion = computed(() => {
305321 return pkg .value .versions [latestTag ] ?? null
306322})
307323
324+ // Detect license changes between current version and latest
325+ const licenseChanged = computed (() => {
326+ const currentLicense = displayVersion .value ?.license
327+ const latestLicense = latestVersion .value ?.license ?? pkg .value ?.license
328+ if (! currentLicense || ! latestLicense ) return false
329+ // Normalize: compare string representations
330+ const normalize = (l : unknown ): string =>
331+ typeof l === ' string' ? l : (l as { type? : string })?.type ?? ' '
332+ return normalize (currentLicense ) !== normalize (latestLicense )
333+ })
334+
308335const deprecationNotice = computed (() => {
309336 if (! displayVersion .value ?.deprecated ) return null
310337
@@ -582,9 +609,19 @@ const showSkeleton = shallowRef(false)
582609 <dt class =" text-xs text-fg-subtle uppercase tracking-wider" >
583610 {{ $t('package.stats.license') }}
584611 </dt >
585- <dd class =" font-mono text-sm text-fg" >
586- <LicenseDisplay v-if =" pkg.license" :license =" pkg.license" />
612+ <dd class =" font-mono text-sm text-fg flex items-center gap-2 flex-wrap " >
613+ <LicenseDisplay v-if =" displayVersion?.license ?? pkg.license" :license =" displayVersion?.license ?? pkg.license" />
587614 <span v-else >{{ $t('package.license.none') }}</span >
615+ <TooltipApp
616+ v-if =" licenseChanged"
617+ :text =" $t('package.license.changed', { latest: latestVersion?.license ?? pkg.license })"
618+ position =" bottom"
619+ >
620+ <span class =" inline-flex items-center gap-1 px-1.5 py-0.5 text-2xs font-sans rounded bg-amber-500/15 text-amber-700 dark:text-amber-400 border border-amber-500/30 cursor-help" >
621+ <span class =" i-lucide:triangle-alert w-3 h-3" aria-hidden =" true" />
622+ {{ $t('package.license.changed_badge') }}
623+ </span >
624+ </TooltipApp >
588625 </dd >
589626 </div >
590627
@@ -959,7 +996,7 @@ const showSkeleton = shallowRef(false)
959996
960997 <!-- Dependencies -->
961998 <PackageDependencies
962- v-if =" hasDependencies && resolvedVersion && displayVersion"
999+ v-if =" resolvedVersion && displayVersion"
9631000 :package-name =" pkg.name"
9641001 :version =" resolvedVersion"
9651002 :dependencies =" displayVersion.dependencies"
@@ -1107,7 +1144,7 @@ const showSkeleton = shallowRef(false)
11071144 ' install sidebar'
11081145 ' vulns sidebar'
11091146 ' readme sidebar' ;
1110- grid-template-rows : auto auto auto auto 1 fr ;
1147+ grid-template-rows : auto auto auto auto ;
11111148 }
11121149}
11131150
@@ -1159,6 +1196,7 @@ const showSkeleton = shallowRef(false)
11591196
11601197.areaSidebar {
11611198 grid-area : sidebar;
1199+ align-self : start ;
11621200}
11631201
11641202@media (max-width : 639.9px ) {
0 commit comments