Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3c39d86
feat: add fullscreen mode for charts and new annotator actions (#3371)
graphieros Oct 29, 2026
255ade9
feat(ui): link trust downgrade warnings to version diffs (#3358)
drawliin Oct 30, 2026
5477c91
feat: allow comparing specific versions of packages (#3350)
gameroman Oct 30, 2026
67ca235
ci: do not cancel target branch check to avoid failed checks on main …
trueberryless Oct 30, 2026
3c6fa3d
fix: resolve aliased dependencies against the aliased package (#3298)
KirbyBT Oct 31, 2026
588b539
fix(ui): stop truncated html showing as package description (#3182)
hamedniroomand Oct 31, 2026
87724e6
fix: support link tag for pipe syntax (#3276)
kazupon Oct 31, 2026
b10e483
chore(deps): migrate vitest imports to vite-plus/test (#3377)
cylewaitforit Oct 31, 2026
530995e
fix(docs): scroll to hash after docs render (#3387)
trueberryless Oct 31, 2026
21d9ae2
fix: generate lunaria status before canary deploy (#3385)
trueberryless Oct 31, 2026
72681bc
fix(docs): resolve `{@link}` in member descriptions (#3386)
trueberryless Oct 31, 2026
6b4f05e
fix: correctly handle distinct version requests in `resolveDependency…
gameroman Oct 31, 2026
02602dc
chore(deps): update all linters (#3359)
renovate[bot] Oct 31, 2026
fefe4f6
fix: do not use `force-cache` in `useCachedFetch` (#3389)
gameroman Oct 31, 2026
f6de9fb
chore(deps): update vite+ to v1.4.0 (#3360)
renovate[bot] Nov 1, 2026
c7387a8
feat: provide rss and atom feeds for the blog (#2562)
Kiwow Nov 1, 2026
1645564
fix: stop `i18n:check` adding empty keys (#3392)
gameroman Nov 1, 2026
3dd6987
chore: unescape quotes in `i18n/locales/en.json` (#3393)
gameroman Nov 1, 2026
4f89fe6
chore(deps): update vueuse monorepo to v15 (#3274)
renovate[bot] Nov 1, 2026
9c23e36
fix: expand github rate limit error msg & add retry config when fetch…
WilcoSp Nov 1, 2026
cb1d5a0
docs(ui): add stories for Profile page (#3209)
wingkwong Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/deploy-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ jobs:
# Version of the underlying sfw to use (no `v` prefix): https://github.com/SocketDev/sfw-free/releases
firewall-version: 1.12.0

# Lunaria needs the full git history, but `vercel deploy` uploads the source without `.git`,
# so generate `dist/lunaria/status.json` here and upload it with the source instead.
# https://github.com/npmx-dev/npmx.dev/issues/2785
- run: sfw vp install --filter . --ignore-scripts
- run: vp run build:lunaria

- run: sfw vp i -g vercel@54.12.2
- run: vercel deploy --target=canary
env:
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/enforce-release-source.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ on:
- synchronize
- edited

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false

permissions: {}

jobs:
Expand Down
6 changes: 6 additions & 0 deletions .github/zizmor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ rules:
ignore:
# lunariajs/action has no tag refs; keep the branch commit hash-pinned.
- lunaria.yml:40
concurrency-limits:
ignore:
# Stateless, idempotent check. Concurrency groups cancel superseded pending
# runs even with `cancel-in-progress: false`, which would leave a failed
# check on the release PR's head commit (`main`).
- enforce-release-source.yml
dangerous-triggers:
ignore:
- enforce-release-source.yml
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ Use vue-i18n's built-in formatters for locale-aware formatting:
Write unit tests for core functionality using Vitest:

```typescript
import { describe, it, expect } from 'vitest'
import { describe, it, expect } from 'vite-plus/test'

describe('featureName', () => {
it('should handle expected case', () => {
Expand Down
6 changes: 6 additions & 0 deletions app/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
25 changes: 20 additions & 5 deletions app/components/Changelog/ErrorMsg.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
<script setup lang="ts">
const { pkgName, changelogLink, viewOnGit } = defineProps<{
import type { NuxtError } from 'nuxt/app'
const { pkgName, changelogLink, viewOnGit, error } = defineProps<{
pkgName?: string
changelogLink: string
viewOnGit: string
error?: NuxtError
}>()
</script>

<template>
<div class="w-full flex items-center flex-col gap-2 mt-4">
<p>{{ $t('changelog.error.p1', { package: pkgName }) }}</p>
<i18n-t keypath="changelog.error.p2" tag="p" #viewon>
<LinkBase :to="changelogLink" class="lowercase">{{ viewOnGit }}</LinkBase>
</i18n-t>
<template v-if="isUnghRateLimitError(error)">
<p>{{ $t('changelog.ungh.rate_limit') }}</p>
<p>{{ $t('changelog.ungh.hint') }}</p>
<LinkBase
variant="button-secondary"
to="https://github.com/apps/ungh-app"
classicon="i-simple-icons:github"
>{{ $t('changelog.ungh.install') }}</LinkBase
>
</template>
<template v-else>
<p>{{ $t('changelog.error.p1', { package: pkgName }) }}</p>
<i18n-t keypath="changelog.error.p2" tag="p" #viewon>
<LinkBase :to="changelogLink" class="lowercase">{{ viewOnGit }}</LinkBase>
</i18n-t>
</template>
</div>
</template>
2 changes: 1 addition & 1 deletion app/components/Changelog/Releases.vue
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,5 @@ prefetchComponents('ChangelogCard')
:host="info.host"
/>
</div>
<slot v-else-if="error" name="error"></slot>
<slot v-else-if="error" name="error" :error></slot>
</template>
21 changes: 21 additions & 0 deletions app/components/Chart/AnnotatorIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { getAnnotatorIcon, getAnnotatorStyle, type AnnotatorSlotName } from '~/utils/charts'

const props = defineProps<{
slotName: AnnotatorSlotName
slotProps?: unknown
}>()

const icon = computed(() => getAnnotatorIcon(props.slotName, props.slotProps))
const style = computed(() => getAnnotatorStyle(props.slotName, props.slotProps))
</script>

<template>
<span
v-if="icon"
class="w-6 h-6"
:class="[icon, slotName === 'annotator-action-color' ? null : 'text-fg-subtle']"
:style="style"
aria-hidden="true"
/>
</template>
13 changes: 13 additions & 0 deletions app/components/Chart/FullscreenIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
defineProps<{
isFullscreen: boolean
}>()
</script>

<template>
<span
class="w-6 h-6 text-fg-subtle pointer-events-none"
:class="isFullscreen ? 'i-lucide:minimize' : 'i-lucide:maximize'"
aria-hidden="true"
/>
</template>
55 changes: 47 additions & 8 deletions app/components/Compare/FacetBarChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import('vue-data-ui/style.css')
const props = defineProps<{
values: (FacetValue | null | undefined)[]
packages: string[]
packagesData: ReadonlyArray<PackageComparisonData | null>
label: string
description: string
facetId?: string
facetLoading?: boolean
}>()

Expand Down Expand Up @@ -77,14 +79,47 @@ const isDarkMode = computed(() => resolvedMode.value === 'dark')

const dataset = computed<VueUiHorizontalBarDatasetItem[]>(() => {
if (props.facetLoading) return []
return props.packages.map((name, index) => {

const omitVersion = props.facetId ? shouldHideFacetInVersionComparison(props.facetId) : false

if (!omitVersion) {
// Version-specific facet: keep one bar per entry (with version in label)
return props.packages.flatMap((spec, index) => {
const rawValue = props.values[index]?.raw
const packageData = props.packagesData[index]
if (!packageData) return []
const { name, displayName } = getPackageDisplayInfo(packageData, spec)

return [
{
name: insertLineBreaks(applyEllipsis(displayName)),
value: typeof rawValue === 'number' ? rawValue : 0,
color: isListedFramework(name) ? getFrameworkColor(name) : undefined,
formattedValue: props.values[index]?.display,
},
]
})
}

// Package-level facet: one bar per unique package name
const seen = new Set<string>()
return props.packages.flatMap((spec, index) => {
const packageData = props.packagesData[index]
if (!packageData) return []

const { name } = getPackageDisplayInfo(packageData, spec)
if (seen.has(name)) return []
seen.add(name)

const rawValue = props.values[index]?.raw
return {
name: insertLineBreaks(applyEllipsis(name)),
value: typeof rawValue === 'number' ? rawValue : 0,
color: isListedFramework(name) ? getFrameworkColor(name) : undefined,
formattedValue: props.values[index]?.display,
}
return [
{
name: insertLineBreaks(applyEllipsis(name)),
value: typeof rawValue === 'number' ? rawValue : 0,
color: isListedFramework(name) ? getFrameworkColor(name) : undefined,
formattedValue: props.values[index]?.display,
},
]
})
})

Expand Down Expand Up @@ -117,14 +152,15 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
buttons: {
tooltip: false,
pdf: false,
fullscreen: false,
fullscreen: true,
sort: false,
annotator: false,
table: false,
csv: false,
altCopy: true,
},
buttonTitles: {
fullscreen: $t('package.trends.toggle_fullscreen'),
img: $t('package.trends.download_file', { fileType: 'PNG' }),
svg: $t('package.trends.download_file', { fileType: 'SVG' }),
altCopy: $t('package.trends.copy_alt.button_label'),
Expand Down Expand Up @@ -320,6 +356,9 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
<ChartCopyPngButton :copied="copiedPng" :copying="isCopyingPng" @click="copyChartPng" />
</template>

<template #optionFullscreen="{ isFullscreen }">
<ChartFullscreenIcon :is-fullscreen="isFullscreen" />
</template>
<template #optionImg>
<span class="text-fg-subtle font-mono pointer-events-none">PNG</span>
</template>
Expand Down
98 changes: 27 additions & 71 deletions app/components/Compare/FacetScatterChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type VueUiScatterSeries,
} from 'vue-data-ui/vue-ui-scatter'
import { buildCompareScatterChartDataset } from '~/utils/compare-scatter-chart'
import { copyAltTextForCompareScatterChart } from '~/utils/charts'
import { CHART_ANNOTATOR_SLOTS, copyAltTextForCompareScatterChart } from '~/utils/charts'
import { useColors } from '~/composables/useColors'
import { downloadFileLink } from '~/utils/download'
import { useCopyChartPng } from '~/composables/useCopyChartPng'
Expand Down Expand Up @@ -56,11 +56,19 @@ const isDarkMode = computed(() => resolvedMode.value === 'dark')

const { facetLabels } = useFacetSelection()

const isVersionComparisonComputed = computed(() => isVersionComparison(props.packages))

const chartableFacets = computed(() =>
(
Object.entries(facetLabels.value) as [
ComparisonFacet,
{ label: string; description: string; chartable_scatter: boolean },
{
label: string
description: string
chartable: boolean
chartable_scatter: boolean
formatter?: ((value: number) => string) | undefined
},
][]
)
.map(([name, facet]) => ({
Expand All @@ -69,10 +77,18 @@ const chartableFacets = computed(() =>
description: facet.description,
chartable: facet.chartable_scatter,
}))
.filter(facet => facet.chartable),
.filter(facet => {
if (!facet.chartable) return false
if (isVersionComparisonComputed.value && shouldHideFacetInVersionComparison(facet.name)) {
return false
}
return true
}),
)

const selectedFacetX = ref<ComparisonFacet>('downloads')
const selectedFacetX = ref<ComparisonFacet>(
isVersionComparisonComputed.value ? 'dependencies' : 'downloads',
)
const selectedFacetY = ref<ComparisonFacet>('installSize')

const dataset = computed<VueUiScatterDatasetItem[]>(() =>
Expand Down Expand Up @@ -125,12 +141,13 @@ const config = computed<VueUiScatterConfig>(() => {
buttons: {
tooltip: false,
pdf: false,
fullscreen: false,
fullscreen: true,
table: false,
csv: false,
altCopy: true,
},
buttonTitles: {
fullscreen: $t('package.trends.toggle_fullscreen'),
csv: $t('package.trends.download_file', { fileType: 'CSV' }),
img: $t('package.trends.download_file', { fileType: 'PNG' }),
svg: $t('package.trends.download_file', { fileType: 'SVG' }),
Expand Down Expand Up @@ -465,78 +482,17 @@ onMounted(async () => {
@click="copyChartPng"
/>
</template>
<template #optionFullscreen="{ isFullscreen }">
<ChartFullscreenIcon :is-fullscreen="isFullscreen" />
</template>
<template #optionImg>
<span class="text-fg-subtle font-mono pointer-events-none">PNG</span>
</template>
<template #optionSvg>
<span class="text-fg-subtle font-mono pointer-events-none">SVG</span>
</template>
<template #annotator-action-close>
<span
class="i-lucide:x w-6 h-6 text-fg-subtle"
style="pointer-events: none"
aria-hidden="true"
/>
</template>
<template #annotator-action-color="{ color }">
<span class="i-lucide:palette w-6 h-6" :style="{ color }" aria-hidden="true" />
</template>
<template #annotator-action-draw="{ mode }">
<span
v-if="mode === 'arrow'"
class="i-lucide:move-up-right text-fg-subtle w-6 h-6"
aria-hidden="true"
/>
<span
v-if="mode === 'text'"
class="i-lucide:type text-fg-subtle w-6 h-6"
aria-hidden="true"
/>
<span
v-if="mode === 'line'"
class="i-lucide:pen-line text-fg-subtle w-6 h-6"
aria-hidden="true"
/>
<span
v-if="mode === 'draw'"
class="i-lucide:line-squiggle text-fg-subtle w-6 h-6"
aria-hidden="true"
/>
</template>
<template #annotator-action-undo>
<span
class="i-lucide:undo-2 w-6 h-6 text-fg-subtle"
style="pointer-events: none"
aria-hidden="true"
/>
</template>
<template #annotator-action-redo>
<span
class="i-lucide:redo-2 w-6 h-6 text-fg-subtle"
style="pointer-events: none"
aria-hidden="true"
/>
</template>
<template #annotator-action-delete>
<span
class="i-lucide:trash w-6 h-6 text-fg-subtle"
style="pointer-events: none"
aria-hidden="true"
/>
</template>
<template #optionAnnotator="{ isAnnotator }">
<span
v-if="isAnnotator"
class="i-lucide:pen-off w-6 h-6 text-fg-subtle"
style="pointer-events: none"
aria-hidden="true"
/>
<span
v-else
class="i-lucide:pen w-6 h-6 text-fg-subtle"
style="pointer-events: none"
aria-hidden="true"
/>
<template v-for="slotName in CHART_ANNOTATOR_SLOTS" #[slotName]="slotProps">
<ChartAnnotatorIcon :slot-name="slotName" :slot-props="slotProps" />
</template>
<template #optionAltCopy>
<span
Expand Down
Loading
Loading