Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a61935f
init
AmirSa12 Feb 24, 2026
84fd9ed
Revert "init"
Aslemammad Feb 26, 2026
922ba21
feat(app): switch releases to R2-backed paginated feed
Aslemammad Feb 26, 2026
0b59206
style(app): format commits page and API
Aslemammad Feb 26, 2026
0281675
fix(app): restore runtime config plugin behavior
Aslemammad Feb 26, 2026
0e59a30
Pin default-branch release and label external commits
Aslemammad Feb 26, 2026
776d1a5
Fix installation Octokit usage in release commits API
Aslemammad Feb 26, 2026
254ed35
Add branch badge metadata for releases
Aslemammad Feb 26, 2026
02e0e62
Fetch release branch badge data from GitHub
Aslemammad Feb 26, 2026
3402689
Prefer branch badge over external badge
Aslemammad Feb 26, 2026
10762a1
Resolve branch badge via associated PR fallback
Aslemammad Feb 26, 2026
b524b9d
Keep branch badge stable with long commit messages
Aslemammad Feb 26, 2026
c4f5752
Keep branch badge single-line and aligned with SHA badge
Aslemammad Feb 26, 2026
0a3b665
Render full commit message in release side panel
Aslemammad Feb 26, 2026
052283f
Add HTTP cache headers for release commits endpoint
Aslemammad Feb 26, 2026
cc855fd
Revert full commit message payload and sidebar rendering
Aslemammad Feb 26, 2026
8b63db2
Show pin indicator for pinned release row
Aslemammad Feb 26, 2026
33afd29
Hide external badge on pinned release
Aslemammad Feb 26, 2026
9c1dde7
Use default branch badge for pinned release
Aslemammad Feb 26, 2026
d61335f
Force pinned release badge to default branch
Aslemammad Feb 26, 2026
4eaa241
Use SSR async data for initial commits load
Aslemammad Feb 26, 2026
8a16009
Add cache headers for SSR repo UI pages
Aslemammad Feb 26, 2026
854c442
Hide pagination when no releases are available
Aslemammad Feb 26, 2026
bc33b21
Use request-aware fetch for SSR commits data
Aslemammad Feb 26, 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
3 changes: 1 addition & 2 deletions packages/app/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ useHead({

useSeoMeta({
title: "pkg.pr.new",
description:
"Search repositories on GitHub to list their continuous releases.",
description: "Search repositories to list their continuous releases.",
});
</script>

Expand Down
173 changes: 126 additions & 47 deletions packages/app/app/components/Commits.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,24 @@ const props = defineProps<{
repo: string;
}>();

const data = await $fetch("/api/repo/commits", {
query: {
owner: props.owner,
repo: props.repo,
},
});
const requestFetch = useRequestFetch();

const { data } = await useAsyncData(
`repo-commits:${props.owner}:${props.repo}:page:1`,
() =>
requestFetch("/api/repo/commits", {
query: {
owner: props.owner,
repo: props.repo,
},
}),
);

if (!data) {
if (!data.value) {
throw createError("Could not load Commits");
}

const branch = shallowReactive(data);
const branch = shallowReactive(data.value);

const commitsWithRelease = computed(() =>
branch.target.history.nodes
Expand Down Expand Up @@ -113,58 +119,78 @@ onBeforeUnmount(() => {

// Pagination
const fetching = ref(false);
const fetchMoreForceDisabled = ref(!commitsWithRelease.value.length);
const currentPage = ref(branch.target.history.pageInfo.currentPage || 1);
const totalPages = computed(
() => branch.target.history.pageInfo.totalPages || 1,
);
const hasNextPage = computed(() => currentPage.value < totalPages.value);
const hasPrevPage = computed(() => currentPage.value > 1);

async function fetchMore() {
if (!branch.target.history.pageInfo.hasNextPage) {
return;
const paginationItems = computed<(number | "...")[]>(() => {
const total = totalPages.value;
const page = currentPage.value;
if (total <= 7) {
return Array.from({ length: total }, (_, i) => i + 1);
}

const items: (number | "...")[] = [1];
const start = Math.max(2, page - 1);
const end = Math.min(total - 1, page + 1);

if (start > 2) {
items.push("...");
}
for (let i = start; i <= end; i++) {
items.push(i);
}
if (end < total - 1) {
items.push("...");
}
items.push(total);
return items;
});

async function fetchPage(page: number) {
if (fetching.value) {
return;
}

try {
fetching.value = true;

const cursor = branch.target.history.pageInfo.endCursor;

const result = await $fetch("/api/repo/commits", {
query: {
owner: props.owner,
repo: props.repo,
cursor,
page: String(page),
},
});

const count = commitsWithRelease.value.length;

branch.target = {
...branch.target,
history: {
...branch.target.history,
nodes: [...branch.target.history.nodes, ...result.target.history.nodes],
pageInfo: result.target.history.pageInfo,
},
};

if (count === commitsWithRelease.value.length) {
fetchMoreForceDisabled.value = true;
}
currentPage.value = result.target.history.pageInfo.currentPage || page;
branch.id = result.id;
branch.name = result.name;
branch.target = result.target;
} finally {
fetching.value = false;
}
}

async function goNextPage() {
if (!hasNextPage.value) {
return;
}
await fetchPage(currentPage.value + 1);
}

async function goPrevPage() {
if (!hasPrevPage.value) {
return;
}
await fetchPage(currentPage.value - 1);
}
</script>

<template>
<div class="flex flex-col gap-6">
<div class="text-center flex justify-center items-center gap-1 opacity-80">
Continuous Releases from
<UIcon name="i-ph-git-branch" />
{{ branch.name }}
</div>

<div class="flex flex-col gap-2">
<div
v-for="commit of commitsWithRelease"
Expand All @@ -176,13 +202,42 @@ async function fetchMore() {
aria-role="button"
@click="selectedCommit = commit"
>
<div class="flex items-center gap-2">
<div class="flex items-center gap-2 min-w-0">
<UIcon name="i-ph-git-commit" class="opacity-50 flex-none" />
<span class="truncate">{{ commit.message }}</span>
<span class="opacity-50 flex-none">
<span class="truncate min-w-0 flex-1">{{ commit.message }}</span>
<span class="opacity-50 flex-none whitespace-nowrap">
{{ useTimeAgo(commit.authoredDate) }}
</span>
<span class="flex-1" />
<UIcon
v-if="commit.pinned"
name="i-ph-push-pin"
class="opacity-70 flex-none"
aria-label="Pinned release"
/>
<UButton
v-if="commit.unverified && !commit.branch && !commit.pinned"
color="neutral"
variant="subtle"
size="xs"
:ui="{
base: 'font-mono pointer-events-none whitespace-nowrap',
}"
>
External
</UButton>
<UButton
v-if="commit.branch"
color="neutral"
variant="subtle"
size="xs"
:ui="{
base: 'font-mono pointer-events-none whitespace-nowrap shrink-0 inline-flex items-center',
}"
>
<span class="block max-w-48 truncate whitespace-nowrap">
{{ commit.branch }}
</span>
</UButton>
<UButton
:to="commit.url"
target="_blank"
Expand All @@ -191,7 +246,7 @@ async function fetchMore() {
size="xs"
aria-label="View Commit"
:ui="{
base: 'font-mono',
base: 'font-mono whitespace-nowrap shrink-0 inline-flex items-center',
}"
@click.stop
>
Expand All @@ -202,18 +257,42 @@ async function fetchMore() {
</div>

<div
v-if="
branch.target.history.pageInfo.hasNextPage && !fetchMoreForceDisabled
"
class="flex justify-center"
v-if="commitsWithRelease.length"
class="flex justify-center items-center gap-1 flex-wrap"
>
<UButton
color="neutral"
variant="subtle"
icon="i-ph-caret-left"
:disabled="!hasPrevPage"
:loading="fetching"
@click="goPrevPage()"
>
Prev
</UButton>

<template v-for="(item, index) in paginationItems" :key="`page-${index}`">
<span v-if="item === '...'" class="px-2 text-sm opacity-60">…</span>
<UButton
v-else
:color="item === currentPage ? 'primary' : 'neutral'"
:variant="item === currentPage ? 'solid' : 'subtle'"
:disabled="fetching"
@click="fetchPage(item)"
>
{{ item }}
</UButton>
</template>

<UButton
color="neutral"
variant="subtle"
icon="i-ph-caret-right"
:disabled="!hasNextPage"
:loading="fetching"
@click="fetchMore()"
@click="goNextPage()"
>
Load More
Next
</UButton>
</div>

Expand Down
9 changes: 9 additions & 0 deletions packages/app/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export default defineNuxtConfig({
},
},

routeRules: {
"/~/**": {
headers: {
"cache-control":
"public, max-age=30, s-maxage=120, stale-while-revalidate=300",
},
},
},

runtimeConfig: {
nitro: {
envPrefix: "NITRO_",
Expand Down
Loading
Loading