diff --git a/app/components/Changelog/Card.vue b/app/components/Changelog/Card.vue new file mode 100644 index 0000000000..bb0cb54728 --- /dev/null +++ b/app/components/Changelog/Card.vue @@ -0,0 +1,71 @@ + + + + diff --git a/app/components/Changelog/ErrorMsg.vue b/app/components/Changelog/ErrorMsg.vue new file mode 100644 index 0000000000..de107979ac --- /dev/null +++ b/app/components/Changelog/ErrorMsg.vue @@ -0,0 +1,15 @@ + + diff --git a/app/components/Changelog/Markdown.vue b/app/components/Changelog/Markdown.vue new file mode 100644 index 0000000000..0eb063442f --- /dev/null +++ b/app/components/Changelog/Markdown.vue @@ -0,0 +1,46 @@ + + diff --git a/app/components/Changelog/Releases.vue b/app/components/Changelog/Releases.vue new file mode 100644 index 0000000000..e455c8de76 --- /dev/null +++ b/app/components/Changelog/Releases.vue @@ -0,0 +1,67 @@ + + diff --git a/app/components/Package/Header.vue b/app/components/Package/Header.vue index db6ed11835..49ebeec038 100644 --- a/app/components/Package/Header.vue +++ b/app/components/Package/Header.vue @@ -5,6 +5,7 @@ import { useModal } from '~/composables/useModal' import { useAtproto } from '~/composables/atproto/useAtproto' import { togglePackageLike } from '~/utils/atproto/likes' import { isEditableElement } from '~/utils/input' +import { usePackageChangelog } from '~/composables/usePackageChangelog' const props = defineProps<{ pkg?: Pick | null @@ -13,7 +14,7 @@ const props = defineProps<{ latestVersion?: SlimVersion | null provenanceData?: ProvenanceDetails | null provenanceStatus?: string | null - page: 'main' | 'docs' | 'code' | 'diff' + page: 'main' | 'docs' | 'code' | 'diff' | 'changes' versionUrlPattern: string }>() @@ -126,6 +127,15 @@ const diffLink = computed((): RouteLocationRaw | null => { return diffRoute(props.pkg.name, props.resolvedVersion, props.latestVersion.version) }) +const { data: changelog } = usePackageChangelog(packageName, requestedVersion) + +const changelogLink = computed((): RouteLocationRaw | null => { + if (!changelog.value || props.pkg == null || props.resolvedVersion == null) { + return null + } + return changelogRoute(props.pkg.name, props.resolvedVersion) +}) + const keyboardShortcuts = useKeyboardShortcuts() onKeyStroke( @@ -180,6 +190,16 @@ onKeyStroke( { dedupe: true }, ) +onKeyStroke( + e => keyboardShortcuts.value && isKeyWithoutModifiers(e, '-') && !isEditableElement(e.target), + e => { + if (changelogLink.value === null) return + e.preventDefault() + navigateTo(changelogLink.value) + }, + { dedupe: true }, +) + //atproto // TODO: Maybe set this where it's not loaded here every load? const { user } = useAtproto() @@ -428,6 +448,15 @@ const likeAction = async () => { > {{ $t('compare.compare_versions') }} + + {{ $t('package.links.changelog') }} + diff --git a/app/components/Readme.vue b/app/components/Readme.vue index c571e63bc2..238742ce36 100644 --- a/app/components/Readme.vue +++ b/app/components/Readme.vue @@ -150,7 +150,7 @@ function handleClick(event: MouseEvent) { @apply inline i-lucide:external-link rtl-flip ms-1 opacity-50; } -.readme :deep(:is(h1, h2, h3, h4, h5, h6) a[href^='#']::after) { +.readme :deep(:is(h1, h2, h3, h4, h5, h6) a[href^='#']:not([content-none])::after) { /* I don't know what kind of sorcery this is, but it ensures this icon can't wrap to a new line on its own. */ content: '__'; @apply inline i-lucide:link rtl-flip ms-1 opacity-0; diff --git a/app/composables/usePackageChangelog.ts b/app/composables/usePackageChangelog.ts new file mode 100644 index 0000000000..7d3dcd9431 --- /dev/null +++ b/app/composables/usePackageChangelog.ts @@ -0,0 +1,13 @@ +import type { ChangelogInfo } from '~~/shared/types/changelog' + +export function usePackageChangelog( + packageName: MaybeRefOrGetter, + version?: MaybeRefOrGetter, +) { + return useLazyFetch(() => { + const name = toValue(packageName) + const ver = toValue(version) + const base = `/api/changelog/info/${name}` + return ver ? `${base}/v/${ver}` : base + }) +} diff --git a/app/composables/useProviderIcon.ts b/app/composables/useProviderIcon.ts new file mode 100644 index 0000000000..4da402f42d --- /dev/null +++ b/app/composables/useProviderIcon.ts @@ -0,0 +1,24 @@ +import type { ProviderId } from '#imports' +import type { IconClass } from '~/types/icon' +import { computed, toValue } from 'vue' + +const PROVIDER_ICONS: Record = { + github: 'i-simple-icons:github', + gitlab: 'i-simple-icons:gitlab', + bitbucket: 'i-simple-icons:bitbucket', + codeberg: 'i-simple-icons:codeberg', + gitea: 'i-simple-icons:gitea', + forgejo: 'i-simple-icons:forgejo', + gitee: 'i-simple-icons:gitee', + sourcehut: 'i-simple-icons:sourcehut', + tangled: 'i-custom:tangled', + radicle: 'i-lucide:network', // Radicle is a P2P network, using network icon +} + +export function useProviderIcon(provider: MaybeRefOrGetter) { + return computed((): IconClass => { + const uProvider = toValue(provider) + if (!uProvider) return 'i-simple-icons:github' + return PROVIDER_ICONS[uProvider] ?? 'i-lucide:code' + }) +} diff --git a/app/pages/package-changes/[[org]]/[name].vue b/app/pages/package-changes/[[org]]/[name].vue new file mode 100644 index 0000000000..ceb8e17252 --- /dev/null +++ b/app/pages/package-changes/[[org]]/[name].vue @@ -0,0 +1,193 @@ + + + + diff --git a/app/pages/package-changes/[[org]]/[name]/v/[version].vue b/app/pages/package-changes/[[org]]/[name]/v/[version].vue new file mode 100644 index 0000000000..6bb4f9ab88 --- /dev/null +++ b/app/pages/package-changes/[[org]]/[name]/v/[version].vue @@ -0,0 +1,11 @@ + + diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index 3af5415e5f..7e7260a7ae 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -1,6 +1,5 @@