Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 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
7adae58
fix(ui): keep tooltip from reappearing after opening a link in a new …
2u841r Nov 1, 2026
87490e5
fix(ui): make the section heading toggle the section (#3278)
golden-fox07 Nov 1, 2026
08bf4f5
feat: use `--before` to show old versions in package timeline (#3264)
nicolo-ribaudo Nov 1, 2026
2a8b1f0
feat: linkify package names in deprecation notices (#3293)
btea Nov 1, 2026
fb85cc3
chore(deps): update dependency simple-git to v4 (#3292)
renovate[bot] Nov 1, 2026
31fcf7b
feat(ui): paginate the timeline around the selected version (#3203)
antfu 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>
85 changes: 50 additions & 35 deletions app/components/CollapsibleSection.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { shallowRef, computed } from 'vue'
import { LinkBase } from '#components'
import { shallowRef } from 'vue'
import type { IconClass } from '~/types/icon'

interface Props {
Expand Down Expand Up @@ -62,15 +61,14 @@ function toggle() {
appSettings.settings.value.sidebar.collapsed.join(' ')
}

const ariaLabel = computed(() => {
if (!props.title) {
return isOpen.value ? $t('common.collapse') : $t('common.expand')
}
const { copied: linkCopied, copy: copyLink } = useClipboard({ copiedDuring: 2000 })

function copySectionLink() {
const url = new URL(window.location.href)
url.hash = props.id
copyLink(url.toString())
}

return isOpen.value
? $t('common.collapse_with_name', { name: props.title })
: $t('common.expand_with_name', { name: props.title })
})
useHead({
style: [
{
Expand All @@ -87,38 +85,55 @@ useHead({
<template>
<section :id="id" :data-anchor-id="id" class="scroll-mt-20 xl:scroll-mt-0">
<div class="flex items-center justify-between mb-3 ps-1">
<component
:is="headingLevel"
class="group text-xs text-fg-subtle uppercase tracking-wider flex gap-2"
:class="subtitle ? 'items-start' : 'items-center'"
>
<div class="flex gap-1 min-w-0" :class="subtitle ? 'items-start' : 'items-center'">
<component
:is="headingLevel"
class="text-xs text-fg-subtle uppercase tracking-wider min-w-0"
>
<button
:id="buttonId"
type="button"
class="cursor-pointer flex items-center gap-2 text-start uppercase text-fg-subtle hover:text-fg-muted transition-colors duration-200 focus-visible:outline-accent/70 rounded"
:aria-expanded="isOpen"
:aria-controls="contentId"
@click="toggle"
>
<span
v-if="isLoading"
class="i-svg-spinners:ring-resize w-3 h-3 shrink-0"
aria-hidden="true"
/>
<span
v-else
class="w-3 h-3 shrink-0 transition-transform duration-200"
:class="isOpen ? 'i-lucide:chevron-down' : 'i-lucide:chevron-right'"
aria-hidden="true"
/>
{{ title }}
</button>
<!-- Aligned with the title, past the chevron (w-3) and its gap (gap-2) -->
<span v-if="subtitle" class="block ps-5 text-2xs normal-case tracking-normal">{{
subtitle
}}</span>
</component>

<button
:id="buttonId"
type="button"
class="cursor-pointer size-5 -me-1 flex items-center justify-center text-fg-subtle hover:text-fg-muted transition-colors duration-200 shrink-0 focus-visible:outline-accent/70 rounded"
:aria-expanded="isOpen"
:aria-controls="contentId"
:aria-label="ariaLabel"
@click="toggle"
class="cursor-pointer size-5 shrink-0 flex items-center justify-center text-fg-subtle hover:text-fg-muted transition-colors duration-200 focus-visible:outline-accent/70 rounded"
:aria-label="
linkCopied
? $t('common.section_link_copied')
: $t('common.copy_section_link', { name: title })
"
@click="copySectionLink"
>
<span v-if="isLoading" class="i-svg-spinners:ring-resize w-3 h-3" aria-hidden="true" />
<span
v-else
class="w-3 h-3 transition-transform duration-200"
:class="isOpen ? 'i-lucide:chevron-down' : 'i-lucide:chevron-right'"
class="w-3 h-3"
:class="linkCopied ? 'i-lucide:check' : 'i-lucide:link'"
aria-hidden="true"
/>
</button>

<span>
<LinkBase :to="`#${id}`">
{{ title }}
</LinkBase>
<span v-if="subtitle" class="block text-2xs normal-case tracking-normal">{{
subtitle
}}</span>
</span>
</component>
</div>

<!-- Actions slot for buttons or other elements -->
<div class="pe-1">
Expand Down
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
Loading
Loading