- >
- );
-}
-
export default function DiscoverClient({
initialData,
initialUrl,
diff --git a/src/app/discover/error.tsx b/src/app/discover/error.tsx
index 6ebb9ba..ca6519c 100644
--- a/src/app/discover/error.tsx
+++ b/src/app/discover/error.tsx
@@ -2,6 +2,7 @@
import { useEffect } from 'react';
+import { ErrorActions } from '@/components/error-actions';
import { captureError } from '@/lib/foundry-monitoring';
export default function DiscoverError({
@@ -23,18 +24,7 @@ export default function DiscoverError({
Something went wrong while loading the discover feed — try again.
-
-
-
-
- {error.digest ?
Reference: {error.digest}
: null}
+
);
diff --git a/src/app/error.tsx b/src/app/error.tsx
index 1c9c424..7613112 100644
--- a/src/app/error.tsx
+++ b/src/app/error.tsx
@@ -2,6 +2,7 @@
import { useEffect } from 'react';
+import { ErrorActions } from '@/components/error-actions';
import { captureError } from '@/lib/foundry-monitoring';
export default function Error({
@@ -25,18 +26,7 @@ export default function Error({
An unexpected error occurred on our end. Your data is safe — try again, and if it keeps
happening, come back in a few minutes.
-
-
-
-
- {error.digest ?
Reference: {error.digest}
: null}
+
);
diff --git a/src/app/stars/error.tsx b/src/app/stars/error.tsx
index d7091d8..185d4bd 100644
--- a/src/app/stars/error.tsx
+++ b/src/app/stars/error.tsx
@@ -2,6 +2,7 @@
import { useEffect } from 'react';
+import { ErrorActions } from '@/components/error-actions';
import { captureError } from '@/lib/foundry-monitoring';
export default function StarsError({
@@ -23,18 +24,7 @@ export default function StarsError({
Something went wrong while loading your starred repos. Your data is safe — try again.
-
-
-
-
- {error.digest ?
Reference: {error.digest}
: null}
+
);
diff --git a/src/app/stars/page.tsx b/src/app/stars/page.tsx
index 4f71358..5d02614 100644
--- a/src/app/stars/page.tsx
+++ b/src/app/stars/page.tsx
@@ -10,13 +10,13 @@ import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
import { ActiveFilterChips } from '@/components/active-filter-chips';
import { BulkActionBar } from '@/components/bulk-action-bar';
import { CompareSheet } from '@/components/compare-sheet';
+import { PageSkeleton } from '@/components/page-skeleton';
import { RepoGrid } from '@/components/repo-grid';
import { Sidebar } from '@/components/sidebar';
import { SyncAnimation, SyncProgressBar } from '@/components/sync-animation';
import { TopBar } from '@/components/top-bar';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Sheet, SheetContent, SheetDescription, SheetTitle } from '@/components/ui/sheet';
-import { Skeleton } from '@/components/ui/skeleton';
import { useLists } from '@/hooks/use-lists';
import { useStarredRepos } from '@/hooks/use-starred-repos';
import { summarizeSyncRepoNames } from '@/lib/sync-performance';
@@ -29,64 +29,6 @@ const sortOptions = [
'name-az',
] as const;
-function PageSkeleton() {
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- {Array.from({ length: 6 }).map((_, i) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
- >
- );
-}
-
export default function StarsPage() {
const { status } = useSession();
const router = useRouter();
diff --git a/src/components/error-actions.tsx b/src/components/error-actions.tsx
new file mode 100644
index 0000000..8bede0d
--- /dev/null
+++ b/src/components/error-actions.tsx
@@ -0,0 +1,29 @@
+/**
+ * Shared error-recovery actions rendered by the per-route `error.tsx`
+ * boundaries. The "Try again" + "Home" button row and optional digest
+ * reference are identical across every error surface.
+ */
+export function ErrorActions({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ return (
+ <>
+
+
+
+
+ {error.digest ?
Reference: {error.digest}
: null}
+ >
+ );
+}
diff --git a/src/components/page-skeleton.tsx b/src/components/page-skeleton.tsx
new file mode 100644
index 0000000..7452f92
--- /dev/null
+++ b/src/components/page-skeleton.tsx
@@ -0,0 +1,64 @@
+import { Skeleton } from '@/components/ui/skeleton';
+
+/**
+ * Shared loading skeleton for the stars and discover list surfaces.
+ * Both pages render an identical header + sidebar + card grid placeholder
+ * while session or data is resolving.
+ */
+export function PageSkeleton() {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: 6 }).map((_, i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+ >
+ );
+}
diff --git a/src/db/seed-embeddings.ts b/src/db/seed-embeddings.ts
index 9fbd9a4..9bc78a7 100644
--- a/src/db/seed-embeddings.ts
+++ b/src/db/seed-embeddings.ts
@@ -1,6 +1,6 @@
import type { InStatement } from './client';
import { createD1RestClientFromEnv } from './rest-client';
-import { buildRepoEmbeddingText, generateEmbeddings, textHash } from '../lib/embeddings';
+import { buildEmbeddingFromRow, generateEmbeddings } from '../lib/embeddings';
import { createVectorizeRestWriterFromEnv } from '../lib/repo-vectors-rest';
const BATCH_SIZE = 50;
@@ -44,22 +44,7 @@ async function seed() {
const toEmbed: { id: number; text: string; hash: string }[] = [];
for (const row of repos.rows) {
- const text = buildRepoEmbeddingText({
- full_name: row.full_name as string,
- description: row.description as string | null,
- language: row.language as string | null,
- topics: row.topics as string,
- ai: row.summary
- ? {
- summary: row.summary as string,
- category: row.category as string,
- subcategories: row.subcategories as string,
- use_cases: row.use_cases as string,
- keywords: row.keywords as string,
- }
- : null,
- });
- const hash = textHash(text);
+ const { text, hash } = buildEmbeddingFromRow(row);
if (existingHashes.get(row.id as number) !== hash) {
toEmbed.push({ id: row.id as number, text, hash });
}
diff --git a/src/lib/embeddings.ts b/src/lib/embeddings.ts
index fc4892b..70b688f 100644
--- a/src/lib/embeddings.ts
+++ b/src/lib/embeddings.ts
@@ -185,6 +185,34 @@ function parseStringList(value: string | string[] | null | undefined): string[]
}
}
+/**
+ * Build embedding text + hash from a D1 repo row. Consolidates the identical
+ * row-to-text mapping used by the embed-pending route, seed-embeddings, and
+ * seed-popular jobs. The row is `Record` because D1 returns
+ * untyped rows; casts happen once inside the helper.
+ */
+export function buildEmbeddingFromRow(row: Record): {
+ text: string;
+ hash: string;
+} {
+ const text = buildRepoEmbeddingText({
+ full_name: row.full_name as string,
+ description: row.description as string | null,
+ language: row.language as string | null,
+ topics: row.topics as string,
+ ai: row.summary
+ ? {
+ summary: row.summary as string,
+ category: row.category as string,
+ subcategories: row.subcategories as string,
+ use_cases: row.use_cases as string,
+ keywords: row.keywords as string,
+ }
+ : null,
+ });
+ return { text, hash: textHash(text) };
+}
+
/** Simple hash to detect when repo text changes. */
export function textHash(text: string): string {
let h = 0;
From 8ff1450f9fca16b8aaa3fcf3ae823ba52bb3b114 Mon Sep 17 00:00:00 2001
From: Sarthak Agrawal
Date: Sat, 15 Aug 2026 04:43:16 +0530
Subject: [PATCH 2/2] fix: correct homepage structured-data identity
---
src/app/layout.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 2fcb9ff..f4fb1d6 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -72,8 +72,9 @@ export default function RootLayout({
{/* fleet-jsonld:start (generated by apply-agent-surfaces; edit registry, not this) */}
{/* fleet-jsonld:end */}