From 3c39d86de72b352e03736969cf84cabdffa22942 Mon Sep 17 00:00:00 2001
From: Alec Lloyd Probert <55991794+graphieros@users.noreply.github.com>
Date: Thu, 29 Oct 2026 22:28:51 +0000
Subject: [PATCH 01/27] feat: add fullscreen mode for charts and new annotator
actions (#3371)
---
app/assets/main.css | 6 ++
app/components/Chart/AnnotatorIcon.vue | 21 +++++
app/components/Chart/FullscreenIcon.vue | 13 +++
app/components/Compare/FacetBarChart.vue | 6 +-
app/components/Compare/FacetScatterChart.vue | 76 ++--------------
.../Package/DailyDownloadsHeatmap.vue | 25 ++----
app/components/Package/TimelineChart.vue | 35 +++-----
app/components/Package/TrendsChart.vue | 78 +++-------------
.../Package/VersionDistribution.vue | 89 +++----------------
app/utils/charts.ts | 16 +++-
i18n/locales/en.json | 1 +
i18n/locales/fr-FR.json | 1 +
i18n/schema.json | 3 +
package.json | 2 +-
pnpm-lock.yaml | 10 +--
pnpm-workspace.yaml | 2 +-
shared/utils/trends-chart.ts | 3 +-
test/nuxt/a11y.spec.ts | 44 +++++++++
uno.config.ts | 2 +
19 files changed, 171 insertions(+), 262 deletions(-)
create mode 100644 app/components/Chart/AnnotatorIcon.vue
create mode 100644 app/components/Chart/FullscreenIcon.vue
diff --git a/app/assets/main.css b/app/assets/main.css
index bb6b830399..bce110f018 100644
--- a/app/assets/main.css
+++ b/app/assets/main.css
@@ -395,3 +395,9 @@ html:has(dialog:modal) {
overflow: hidden;
scrollbar-gutter: stable;
}
+
+/* Match main annotator toolbar backdrop when group buttons are disabled
+ Override the background color set by vue-data-ui */
+.vue-ui-pen-and-paper-group-actions {
+ background-color: transparent !important;
+}
diff --git a/app/components/Chart/AnnotatorIcon.vue b/app/components/Chart/AnnotatorIcon.vue
new file mode 100644
index 0000000000..84ce04d38a
--- /dev/null
+++ b/app/components/Chart/AnnotatorIcon.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/app/components/Chart/FullscreenIcon.vue b/app/components/Chart/FullscreenIcon.vue
new file mode 100644
index 0000000000..49a1ce9287
--- /dev/null
+++ b/app/components/Chart/FullscreenIcon.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/app/components/Compare/FacetBarChart.vue b/app/components/Compare/FacetBarChart.vue
index 276770dc46..6079e59706 100644
--- a/app/components/Compare/FacetBarChart.vue
+++ b/app/components/Compare/FacetBarChart.vue
@@ -117,7 +117,7 @@ const config = computed{{ $t('package.get_started.title') }}
package@version
+
+
+ vue@3.0.0
+
+
+ vue@3.5.44
+
+
{{
$t('compare.selector.packages_selected', {
diff --git a/app/composables/useCompareReplacements.ts b/app/composables/useCompareReplacements.ts
index eda1d9f356..c578e06c5a 100644
--- a/app/composables/useCompareReplacements.ts
+++ b/app/composables/useCompareReplacements.ts
@@ -1,4 +1,6 @@
import type { ModuleReplacement, ModuleReplacementMapping } from 'module-replacements'
+import { NO_DEPENDENCY_ID } from '#shared/utils/compare'
+import { parsePackageSpec } from '#shared/utils/parse-package-param'
export interface ReplacementSuggestion {
forPackage: string
@@ -31,6 +33,12 @@ export function useCompareReplacements(packageNames: MaybeRefOrGetter {{ $t('changelog.error.p1', { package: pkgName }) }} {{ $t('changelog.ungh.rate_limit') }} {{ $t('changelog.ungh.hint') }} {{ $t('changelog.error.p1', { package: pkgName }) }}
- {{ $t('changelog.rate_limit_ungh') }}
- {{ $t('changelog.ungh.rate_limit') }} {{ $t('changelog.ungh.hint') }}
{{ $t('changelog.version_unavailable') }}
like with Vue's releases
- html: html?.replace(/(?)\n/g, ' like with Vue's releases
+ html: html?.replace(/(?)\n/g, '
') ?? null,
- title: r.name || r.tag,
- draft: r.draft,
- prerelease: r.prerelease,
- toc,
- publishedAt: r.publishedAt,
- link: `https://github.com/${owner}/${repo}/releases/tag/${r.tag}`,
- tag: r.tag,
- } satisfies ReleaseData
- })
+ try {
+ const data = await $fetch(`https://ungh.cc/repos/${owner}/${repo}/releases`, {
+ headers: {
+ 'Accept': '*/*',
+ 'User-Agent': 'npmx.dev',
+ },
+ // with 403/429, the specific token from ungh could be exhausted, so we retry in case a different one does work
+ retryDelay: 300,
+ retry: 3,
+ // only 403 has been added, others status codes are defaults from ofetch
+ retryStatusCodes: [403, 408, 409, 425, 429, 500, 502, 503, 504],
+ timeout: TIMEOUT_TIME,
+ })
+
+ const { releases } = v.parse(GithubReleaseCollectionSchama, data)
+
+ const render = await changelogRenderer(createGithubRepoInfo(owner, repo))
+
+ return releases.map(r => {
+ const { html, toc } = render(r.markdown, r.id)
+ return {
+ id: r.id,
+ // replace single \n within
') ?? null,
+ title: r.name || r.tag,
+ draft: r.draft,
+ prerelease: r.prerelease,
+ toc,
+ publishedAt: r.publishedAt,
+ link: `https://github.com/${owner}/${repo}/releases/tag/${r.tag}`,
+ tag: r.tag,
+ } satisfies ReleaseData
+ })
+ } catch (error) {
+ if (error instanceof FetchError && (error.statusCode == 403 || error.statusCode == 429)) {
+ throw createError({
+ statusCode: 502,
+ message: ERROR_UNGH_API_KEY_EXHAUSTED,
+ statusMessage: ERROR_UNGH_API_KEY_EXHAUSTED,
+ })
+ }
+ throw error
+ }
}
async function getReleasesFromForgejo(owner: string, repo: string, host: string) {
diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts
index f55e04b8c7..15193dceaf 100644
--- a/server/utils/changelog/detectChangelog.ts
+++ b/server/utils/changelog/detectChangelog.ts
@@ -210,6 +210,11 @@ async function checkLatestGithubRelease(
'User-Agent': 'npmx.dev',
},
timeout: TIMEOUT,
+ // with 403/429, the specific token from ungh could be exhausted, so we retry in case a different one does work
+ retryDelay: 300,
+ retry: 3,
+ // only 403 has been added, others status codes are defaults from ofetch
+ retryStatusCodes: [403, 408, 409, 425, 429, 500, 502, 503, 504],
},
)
@@ -258,6 +263,7 @@ async function checkLatestGithubRelease(
if (e.statusCode === 403 || e.statusCode === 429) {
throw createError({
statusCode: 502,
+ message: ERROR_UNGH_API_KEY_EXHAUSTED,
statusMessage: ERROR_UNGH_API_KEY_EXHAUSTED,
})
}
diff --git a/test/unit/app/utils/changelog-errors.spec.ts b/test/unit/app/utils/changelog-errors.spec.ts
new file mode 100644
index 0000000000..f3d101f96b
--- /dev/null
+++ b/test/unit/app/utils/changelog-errors.spec.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from 'vite-plus/test'
+import type { NuxtError } from 'nuxt/app'
+import { ERROR_UNGH_API_KEY_EXHAUSTED } from '../../../../shared/utils/constants'
+import { isUnghRateLimitError } from '../../../../app/utils/changelog-errors'
+
+function nuxtError(fields: Partial